@zereight/mcp-gitlab 2.1.25 → 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 +631 -323
- package/build/oauth.js +65 -3
- package/build/schemas.js +474 -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 +25 -2
- package/build/utils/forwarded-public-base-url.js +62 -0
- package/build/utils/schema.js +15 -1
- package/package.json +2 -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
|
}
|
|
@@ -5742,7 +5746,8 @@ async function myIssues(options = {}) {
|
|
|
5742
5746
|
effectiveProjectId = getEffectiveProjectId(options.project_id || "");
|
|
5743
5747
|
}
|
|
5744
5748
|
catch (err) {
|
|
5745
|
-
if (err instanceof Error &&
|
|
5749
|
+
if (err instanceof Error &&
|
|
5750
|
+
err.message.includes("No project ID provided and GITLAB_PROJECT_ID is not set")) {
|
|
5746
5751
|
effectiveProjectId = "";
|
|
5747
5752
|
}
|
|
5748
5753
|
else {
|
|
@@ -5956,7 +5961,11 @@ async function updateGroupVariable(groupId, key, options) {
|
|
|
5956
5961
|
if (filter?.environment_scope) {
|
|
5957
5962
|
url.searchParams.append("filter[environment_scope]", filter.environment_scope);
|
|
5958
5963
|
}
|
|
5959
|
-
const response = await fetch(url.toString(), {
|
|
5964
|
+
const response = await fetch(url.toString(), {
|
|
5965
|
+
...getFetchConfig(),
|
|
5966
|
+
method: "PUT",
|
|
5967
|
+
body: JSON.stringify(body),
|
|
5968
|
+
});
|
|
5960
5969
|
await handleGitLabError(response);
|
|
5961
5970
|
const data = await response.json();
|
|
5962
5971
|
return GitLabCiVariableSchema.parse(data);
|
|
@@ -6004,7 +6013,9 @@ async function getDependencyProxySettings(groupPath) {
|
|
|
6004
6013
|
});
|
|
6005
6014
|
}
|
|
6006
6015
|
async function updateDependencyProxySettings(groupPath, options) {
|
|
6007
|
-
if (options.enabled === undefined &&
|
|
6016
|
+
if (options.enabled === undefined &&
|
|
6017
|
+
options.identity === undefined &&
|
|
6018
|
+
options.secret === undefined) {
|
|
6008
6019
|
throw new Error("At least one of enabled, identity, or secret must be provided");
|
|
6009
6020
|
}
|
|
6010
6021
|
const fullPath = await resolveGroupFullPath(groupPath);
|
|
@@ -6038,7 +6049,11 @@ async function listDependencyProxyBlobs(groupPath, options = {}) {
|
|
|
6038
6049
|
if (!conn)
|
|
6039
6050
|
throw new Error(`Group not found or dependency proxy not enabled: ${fullPath}`);
|
|
6040
6051
|
return {
|
|
6041
|
-
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
|
+
})),
|
|
6042
6057
|
pageInfo: conn.pageInfo,
|
|
6043
6058
|
};
|
|
6044
6059
|
}
|
|
@@ -6098,7 +6113,7 @@ async function markdownUpload(projectId, filePath, content, filename) {
|
|
|
6098
6113
|
const response = await fetch(url.toString(), {
|
|
6099
6114
|
...defaultFetchConfig,
|
|
6100
6115
|
method: "POST",
|
|
6101
|
-
body: form
|
|
6116
|
+
body: form,
|
|
6102
6117
|
});
|
|
6103
6118
|
if (!response.ok) {
|
|
6104
6119
|
await handleGitLabError(response);
|
|
@@ -6419,6 +6434,34 @@ async function getTagSignature(projectId, tagName) {
|
|
|
6419
6434
|
const data = await response.json();
|
|
6420
6435
|
return GitLabTagSignatureSchema.parse(data);
|
|
6421
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
|
+
}
|
|
6422
6465
|
// Request handlers are now registered inside createServer() factory function
|
|
6423
6466
|
// to ensure each transport connection gets its own Server instance (GHSA-345p-7cg4-v4c7).
|
|
6424
6467
|
async function handleToolCall(params) {
|
|
@@ -6482,7 +6525,7 @@ async function handleToolCall(params) {
|
|
|
6482
6525
|
}
|
|
6483
6526
|
const json = await response.json();
|
|
6484
6527
|
return {
|
|
6485
|
-
content: [{ type: "text", text: JSON.stringify(json
|
|
6528
|
+
content: [{ type: "text", text: JSON.stringify(json) }],
|
|
6486
6529
|
};
|
|
6487
6530
|
}
|
|
6488
6531
|
catch (err) {
|
|
@@ -6491,7 +6534,7 @@ async function handleToolCall(params) {
|
|
|
6491
6534
|
content: [
|
|
6492
6535
|
{
|
|
6493
6536
|
type: "text",
|
|
6494
|
-
text: JSON.stringify({ error: `GraphQL request failed: ${message}` }
|
|
6537
|
+
text: JSON.stringify({ error: `GraphQL request failed: ${message}` }),
|
|
6495
6538
|
},
|
|
6496
6539
|
],
|
|
6497
6540
|
};
|
|
@@ -6506,7 +6549,7 @@ async function handleToolCall(params) {
|
|
|
6506
6549
|
try {
|
|
6507
6550
|
const forkedProject = await forkProject(forkArgs.project_id, forkArgs.namespace);
|
|
6508
6551
|
return {
|
|
6509
|
-
content: [{ type: "text", text: JSON.stringify(forkedProject
|
|
6552
|
+
content: [{ type: "text", text: JSON.stringify(forkedProject) }],
|
|
6510
6553
|
};
|
|
6511
6554
|
}
|
|
6512
6555
|
catch (forkError) {
|
|
@@ -6519,7 +6562,7 @@ async function handleToolCall(params) {
|
|
|
6519
6562
|
content: [
|
|
6520
6563
|
{
|
|
6521
6564
|
type: "text",
|
|
6522
|
-
text: JSON.stringify({ error: forkErrorMessage }
|
|
6565
|
+
text: JSON.stringify({ error: forkErrorMessage }),
|
|
6523
6566
|
},
|
|
6524
6567
|
],
|
|
6525
6568
|
};
|
|
@@ -6536,7 +6579,7 @@ async function handleToolCall(params) {
|
|
|
6536
6579
|
ref,
|
|
6537
6580
|
});
|
|
6538
6581
|
return {
|
|
6539
|
-
content: [{ type: "text", text: JSON.stringify(branch
|
|
6582
|
+
content: [{ type: "text", text: JSON.stringify(branch) }],
|
|
6540
6583
|
};
|
|
6541
6584
|
}
|
|
6542
6585
|
case "get_branch_diffs": {
|
|
@@ -6544,14 +6587,14 @@ async function handleToolCall(params) {
|
|
|
6544
6587
|
const diffResp = await getBranchDiffs(args.project_id, args.from, args.to, args.straight);
|
|
6545
6588
|
diffResp.diffs = filterDiffsByPatterns(diffResp.diffs, args.excluded_file_patterns);
|
|
6546
6589
|
return {
|
|
6547
|
-
content: [{ type: "text", text: JSON.stringify(diffResp
|
|
6590
|
+
content: [{ type: "text", text: JSON.stringify(diffResp) }],
|
|
6548
6591
|
};
|
|
6549
6592
|
}
|
|
6550
6593
|
case "search_repositories": {
|
|
6551
6594
|
const args = SearchRepositoriesSchema.parse(params.arguments);
|
|
6552
6595
|
const results = await searchProjects(args.search, args.page, args.per_page);
|
|
6553
6596
|
return {
|
|
6554
|
-
content: [{ type: "text", text: JSON.stringify(results
|
|
6597
|
+
content: [{ type: "text", text: JSON.stringify(results) }],
|
|
6555
6598
|
};
|
|
6556
6599
|
}
|
|
6557
6600
|
case "search_code": {
|
|
@@ -6565,7 +6608,7 @@ async function handleToolCall(params) {
|
|
|
6565
6608
|
per_page: args.per_page,
|
|
6566
6609
|
});
|
|
6567
6610
|
return {
|
|
6568
|
-
content: [{ type: "text", text: JSON.stringify(results
|
|
6611
|
+
content: [{ type: "text", text: JSON.stringify(results) }],
|
|
6569
6612
|
};
|
|
6570
6613
|
}
|
|
6571
6614
|
case "search_project_code": {
|
|
@@ -6581,7 +6624,7 @@ async function handleToolCall(params) {
|
|
|
6581
6624
|
per_page: args.per_page,
|
|
6582
6625
|
});
|
|
6583
6626
|
return {
|
|
6584
|
-
content: [{ type: "text", text: JSON.stringify(results
|
|
6627
|
+
content: [{ type: "text", text: JSON.stringify(results) }],
|
|
6585
6628
|
};
|
|
6586
6629
|
}
|
|
6587
6630
|
case "search_group_code": {
|
|
@@ -6596,7 +6639,7 @@ async function handleToolCall(params) {
|
|
|
6596
6639
|
per_page: args.per_page,
|
|
6597
6640
|
});
|
|
6598
6641
|
return {
|
|
6599
|
-
content: [{ type: "text", text: JSON.stringify(results
|
|
6642
|
+
content: [{ type: "text", text: JSON.stringify(results) }],
|
|
6600
6643
|
};
|
|
6601
6644
|
}
|
|
6602
6645
|
case "create_repository": {
|
|
@@ -6604,7 +6647,7 @@ async function handleToolCall(params) {
|
|
|
6604
6647
|
const args = CreateRepositorySchema.parse(params.arguments);
|
|
6605
6648
|
const repository = await createRepository(args);
|
|
6606
6649
|
return {
|
|
6607
|
-
content: [{ type: "text", text: JSON.stringify(repository
|
|
6650
|
+
content: [{ type: "text", text: JSON.stringify(repository) }],
|
|
6608
6651
|
};
|
|
6609
6652
|
}
|
|
6610
6653
|
case "create_group": {
|
|
@@ -6631,28 +6674,28 @@ async function handleToolCall(params) {
|
|
|
6631
6674
|
const data = await response.json();
|
|
6632
6675
|
const group = GitLabGroupSchema.parse(data);
|
|
6633
6676
|
return {
|
|
6634
|
-
content: [{ type: "text", text: JSON.stringify(group
|
|
6677
|
+
content: [{ type: "text", text: JSON.stringify(group) }],
|
|
6635
6678
|
};
|
|
6636
6679
|
}
|
|
6637
6680
|
case "get_file_contents": {
|
|
6638
6681
|
const args = GetFileContentsSchema.parse(params.arguments);
|
|
6639
6682
|
const contents = await getFileContents(args.project_id, args.file_path, args.ref);
|
|
6640
6683
|
return {
|
|
6641
|
-
content: [{ type: "text", text: JSON.stringify(contents
|
|
6684
|
+
content: [{ type: "text", text: JSON.stringify(contents) }],
|
|
6642
6685
|
};
|
|
6643
6686
|
}
|
|
6644
6687
|
case "create_or_update_file": {
|
|
6645
6688
|
const args = CreateOrUpdateFileSchema.parse(params.arguments);
|
|
6646
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);
|
|
6647
6690
|
return {
|
|
6648
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
6691
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
6649
6692
|
};
|
|
6650
6693
|
}
|
|
6651
6694
|
case "push_files": {
|
|
6652
6695
|
const args = PushFilesSchema.parse(params.arguments);
|
|
6653
6696
|
const result = await createCommit(args.project_id, args.commit_message, args.branch, args.files.map(f => ({ path: f.file_path, content: f.content })));
|
|
6654
6697
|
return {
|
|
6655
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
6698
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
6656
6699
|
};
|
|
6657
6700
|
}
|
|
6658
6701
|
case "create_issue": {
|
|
@@ -6660,7 +6703,7 @@ async function handleToolCall(params) {
|
|
|
6660
6703
|
const { project_id, ...options } = args;
|
|
6661
6704
|
const issue = await createIssue(project_id, options);
|
|
6662
6705
|
return {
|
|
6663
|
-
content: [{ type: "text", text: JSON.stringify(issue
|
|
6706
|
+
content: [{ type: "text", text: JSON.stringify(issue) }],
|
|
6664
6707
|
};
|
|
6665
6708
|
}
|
|
6666
6709
|
case "create_merge_request": {
|
|
@@ -6668,7 +6711,7 @@ async function handleToolCall(params) {
|
|
|
6668
6711
|
const { project_id, ...options } = args;
|
|
6669
6712
|
const mergeRequest = await createMergeRequest(project_id, options);
|
|
6670
6713
|
return {
|
|
6671
|
-
content: [{ type: "text", text: JSON.stringify(mergeRequest
|
|
6714
|
+
content: [{ type: "text", text: JSON.stringify(mergeRequest) }],
|
|
6672
6715
|
};
|
|
6673
6716
|
}
|
|
6674
6717
|
case "delete_merge_request_discussion_note": {
|
|
@@ -6685,21 +6728,21 @@ async function handleToolCall(params) {
|
|
|
6685
6728
|
args.resolved // Now one of body or resolved must be provided, not both
|
|
6686
6729
|
);
|
|
6687
6730
|
return {
|
|
6688
|
-
content: [{ type: "text", text: JSON.stringify(note
|
|
6731
|
+
content: [{ type: "text", text: JSON.stringify(note) }],
|
|
6689
6732
|
};
|
|
6690
6733
|
}
|
|
6691
6734
|
case "create_merge_request_discussion_note": {
|
|
6692
6735
|
const args = CreateMergeRequestDiscussionNoteSchema.parse(params.arguments);
|
|
6693
6736
|
const note = await createMergeRequestDiscussionNote(args.project_id, args.merge_request_iid, args.discussion_id, args.body, args.created_at);
|
|
6694
6737
|
return {
|
|
6695
|
-
content: [{ type: "text", text: JSON.stringify(note
|
|
6738
|
+
content: [{ type: "text", text: JSON.stringify(note) }],
|
|
6696
6739
|
};
|
|
6697
6740
|
}
|
|
6698
6741
|
case "create_merge_request_note": {
|
|
6699
6742
|
const args = CreateMergeRequestNoteSchema.parse(params.arguments);
|
|
6700
6743
|
const note = await createMergeRequestNote(args.project_id, args.merge_request_iid, args.body);
|
|
6701
6744
|
return {
|
|
6702
|
-
content: [{ type: "text", text: JSON.stringify(note
|
|
6745
|
+
content: [{ type: "text", text: JSON.stringify(note) }],
|
|
6703
6746
|
};
|
|
6704
6747
|
}
|
|
6705
6748
|
case "delete_merge_request_note": {
|
|
@@ -6713,121 +6756,141 @@ async function handleToolCall(params) {
|
|
|
6713
6756
|
const args = GetMergeRequestNoteSchema.parse(params.arguments);
|
|
6714
6757
|
const note = await getMergeRequestNote(args.project_id, args.merge_request_iid, args.note_id);
|
|
6715
6758
|
return {
|
|
6716
|
-
content: [{ type: "text", text: JSON.stringify(note
|
|
6759
|
+
content: [{ type: "text", text: JSON.stringify(note) }],
|
|
6717
6760
|
};
|
|
6718
6761
|
}
|
|
6719
6762
|
case "get_merge_request_notes": {
|
|
6720
6763
|
const args = GetMergeRequestNotesSchema.parse(params.arguments);
|
|
6721
6764
|
const notes = await getMergeRequestNotes(args.project_id, args.merge_request_iid, args.sort, args.order_by, args.per_page, args.page);
|
|
6722
6765
|
return {
|
|
6723
|
-
content: [{ type: "text", text: JSON.stringify(notes
|
|
6766
|
+
content: [{ type: "text", text: JSON.stringify(notes) }],
|
|
6724
6767
|
};
|
|
6725
6768
|
}
|
|
6726
6769
|
case "update_merge_request_note": {
|
|
6727
6770
|
const args = UpdateMergeRequestNoteSchema.parse(params.arguments);
|
|
6728
6771
|
const note = await updateMergeRequestNote(args.project_id, args.merge_request_iid, args.note_id, args.body);
|
|
6729
6772
|
return {
|
|
6730
|
-
content: [{ type: "text", text: JSON.stringify(note
|
|
6773
|
+
content: [{ type: "text", text: JSON.stringify(note) }],
|
|
6731
6774
|
};
|
|
6732
6775
|
}
|
|
6733
6776
|
case "list_merge_request_emoji_reactions": {
|
|
6734
6777
|
const args = ListMergeRequestEmojiReactionsSchema.parse(params.arguments);
|
|
6735
6778
|
const path = buildAwardEmojiPath("merge_requests", args.project_id, args.merge_request_iid);
|
|
6736
6779
|
const result = await listRestAwardEmoji(path);
|
|
6737
|
-
return { content: [{ type: "text", text: JSON.stringify(result
|
|
6780
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
6738
6781
|
}
|
|
6739
6782
|
case "list_merge_request_note_emoji_reactions": {
|
|
6740
6783
|
const args = ListMergeRequestNoteEmojiReactionsSchema.parse(params.arguments);
|
|
6741
6784
|
const path = buildAwardEmojiPath("merge_requests", args.project_id, args.merge_request_iid, { noteId: args.note_id, discussionId: args.discussion_id });
|
|
6742
6785
|
const result = await listRestAwardEmoji(path);
|
|
6743
|
-
return { content: [{ type: "text", text: JSON.stringify(result
|
|
6786
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
6744
6787
|
}
|
|
6745
6788
|
case "create_merge_request_emoji_reaction": {
|
|
6746
6789
|
const args = CreateMergeRequestEmojiReactionSchema.parse(params.arguments);
|
|
6747
6790
|
const path = buildAwardEmojiPath("merge_requests", args.project_id, args.merge_request_iid);
|
|
6748
6791
|
const result = await createRestAwardEmoji(path, args.name);
|
|
6749
|
-
return { content: [{ type: "text", text: JSON.stringify(result
|
|
6792
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
6750
6793
|
}
|
|
6751
6794
|
case "delete_merge_request_emoji_reaction": {
|
|
6752
6795
|
const args = DeleteMergeRequestEmojiReactionSchema.parse(params.arguments);
|
|
6753
6796
|
const path = buildAwardEmojiPath("merge_requests", args.project_id, args.merge_request_iid, { awardId: args.award_id });
|
|
6754
6797
|
await deleteRestAwardEmoji(path);
|
|
6755
|
-
return {
|
|
6798
|
+
return {
|
|
6799
|
+
content: [{ type: "text", text: "Merge request emoji reaction deleted successfully" }],
|
|
6800
|
+
};
|
|
6756
6801
|
}
|
|
6757
6802
|
case "create_merge_request_note_emoji_reaction": {
|
|
6758
6803
|
const args = CreateMergeRequestNoteEmojiReactionSchema.parse(params.arguments);
|
|
6759
6804
|
const path = buildAwardEmojiPath("merge_requests", args.project_id, args.merge_request_iid, { noteId: args.note_id, discussionId: args.discussion_id });
|
|
6760
6805
|
const result = await createRestAwardEmoji(path, args.name);
|
|
6761
|
-
return { content: [{ type: "text", text: JSON.stringify(result
|
|
6806
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
6762
6807
|
}
|
|
6763
6808
|
case "delete_merge_request_note_emoji_reaction": {
|
|
6764
6809
|
const args = DeleteMergeRequestNoteEmojiReactionSchema.parse(params.arguments);
|
|
6765
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 });
|
|
6766
6811
|
await deleteRestAwardEmoji(path);
|
|
6767
|
-
return {
|
|
6812
|
+
return {
|
|
6813
|
+
content: [
|
|
6814
|
+
{ type: "text", text: "Merge request note emoji reaction deleted successfully" },
|
|
6815
|
+
],
|
|
6816
|
+
};
|
|
6768
6817
|
}
|
|
6769
6818
|
case "update_issue_note": {
|
|
6770
6819
|
const args = UpdateIssueNoteSchema.parse(params.arguments);
|
|
6771
6820
|
const note = await updateIssueNote(args.project_id, args.issue_iid, args.discussion_id, args.note_id, args.body, args.resolved);
|
|
6772
6821
|
return {
|
|
6773
|
-
content: [{ type: "text", text: JSON.stringify(note
|
|
6822
|
+
content: [{ type: "text", text: JSON.stringify(note) }],
|
|
6774
6823
|
};
|
|
6775
6824
|
}
|
|
6776
6825
|
case "create_issue_note": {
|
|
6777
6826
|
const args = CreateIssueNoteSchema.parse(params.arguments);
|
|
6778
6827
|
const note = await createIssueNote(args.project_id, args.issue_iid, args.discussion_id, args.body, args.created_at);
|
|
6779
6828
|
return {
|
|
6780
|
-
content: [{ type: "text", text: JSON.stringify(note
|
|
6829
|
+
content: [{ type: "text", text: JSON.stringify(note) }],
|
|
6781
6830
|
};
|
|
6782
6831
|
}
|
|
6783
6832
|
case "list_issue_emoji_reactions": {
|
|
6784
6833
|
const args = ListIssueEmojiReactionsSchema.parse(params.arguments);
|
|
6785
6834
|
const path = buildAwardEmojiPath("issues", args.project_id, args.issue_iid);
|
|
6786
6835
|
const result = await listRestAwardEmoji(path);
|
|
6787
|
-
return { content: [{ type: "text", text: JSON.stringify(result
|
|
6836
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
6788
6837
|
}
|
|
6789
6838
|
case "list_issue_note_emoji_reactions": {
|
|
6790
6839
|
const args = ListIssueNoteEmojiReactionsSchema.parse(params.arguments);
|
|
6791
|
-
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
|
+
});
|
|
6792
6844
|
const result = await listRestAwardEmoji(path);
|
|
6793
|
-
return { content: [{ type: "text", text: JSON.stringify(result
|
|
6845
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
6794
6846
|
}
|
|
6795
6847
|
case "create_issue_emoji_reaction": {
|
|
6796
6848
|
const args = CreateIssueEmojiReactionSchema.parse(params.arguments);
|
|
6797
6849
|
const path = buildAwardEmojiPath("issues", args.project_id, args.issue_iid);
|
|
6798
6850
|
const result = await createRestAwardEmoji(path, args.name);
|
|
6799
|
-
return { content: [{ type: "text", text: JSON.stringify(result
|
|
6851
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
6800
6852
|
}
|
|
6801
6853
|
case "delete_issue_emoji_reaction": {
|
|
6802
6854
|
const args = DeleteIssueEmojiReactionSchema.parse(params.arguments);
|
|
6803
|
-
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
|
+
});
|
|
6804
6858
|
await deleteRestAwardEmoji(path);
|
|
6805
6859
|
return { content: [{ type: "text", text: "Issue emoji reaction deleted successfully" }] };
|
|
6806
6860
|
}
|
|
6807
6861
|
case "create_issue_note_emoji_reaction": {
|
|
6808
6862
|
const args = CreateIssueNoteEmojiReactionSchema.parse(params.arguments);
|
|
6809
|
-
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
|
+
});
|
|
6810
6867
|
const result = await createRestAwardEmoji(path, args.name);
|
|
6811
|
-
return { content: [{ type: "text", text: JSON.stringify(result
|
|
6868
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
6812
6869
|
}
|
|
6813
6870
|
case "delete_issue_note_emoji_reaction": {
|
|
6814
6871
|
const args = DeleteIssueNoteEmojiReactionSchema.parse(params.arguments);
|
|
6815
|
-
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
|
+
});
|
|
6816
6877
|
await deleteRestAwardEmoji(path);
|
|
6817
|
-
return {
|
|
6878
|
+
return {
|
|
6879
|
+
content: [{ type: "text", text: "Issue note emoji reaction deleted successfully" }],
|
|
6880
|
+
};
|
|
6818
6881
|
}
|
|
6819
6882
|
case "list_todos": {
|
|
6820
6883
|
const args = ListTodosSchema.parse(params.arguments);
|
|
6821
6884
|
const todos = await listTodos(args);
|
|
6822
6885
|
return {
|
|
6823
|
-
content: [{ type: "text", text: JSON.stringify(todos
|
|
6886
|
+
content: [{ type: "text", text: JSON.stringify(todos) }],
|
|
6824
6887
|
};
|
|
6825
6888
|
}
|
|
6826
6889
|
case "mark_todo_done": {
|
|
6827
6890
|
const args = MarkTodoDoneSchema.parse(params.arguments);
|
|
6828
6891
|
const todo = await markTodoDone(args.id);
|
|
6829
6892
|
return {
|
|
6830
|
-
content: [{ type: "text", text: JSON.stringify(todo
|
|
6893
|
+
content: [{ type: "text", text: JSON.stringify(todo) }],
|
|
6831
6894
|
};
|
|
6832
6895
|
}
|
|
6833
6896
|
case "mark_all_todos_done": {
|
|
@@ -6861,7 +6924,7 @@ async function handleToolCall(params) {
|
|
|
6861
6924
|
content: [
|
|
6862
6925
|
{
|
|
6863
6926
|
type: "text",
|
|
6864
|
-
text: JSON.stringify(mergeRequestWithDeploymentSummary
|
|
6927
|
+
text: JSON.stringify(mergeRequestWithDeploymentSummary),
|
|
6865
6928
|
},
|
|
6866
6929
|
],
|
|
6867
6930
|
};
|
|
@@ -6871,14 +6934,14 @@ async function handleToolCall(params) {
|
|
|
6871
6934
|
const diffs = await getMergeRequestDiffs(args.project_id, args.merge_request_iid, args.source_branch, args.view);
|
|
6872
6935
|
const filteredDiffs = filterDiffsByPatterns(diffs, args.excluded_file_patterns);
|
|
6873
6936
|
return {
|
|
6874
|
-
content: [{ type: "text", text: JSON.stringify(filteredDiffs
|
|
6937
|
+
content: [{ type: "text", text: JSON.stringify(filteredDiffs) }],
|
|
6875
6938
|
};
|
|
6876
6939
|
}
|
|
6877
6940
|
case "list_merge_request_changed_files": {
|
|
6878
6941
|
const args = ListMergeRequestChangedFilesSchema.parse(params.arguments);
|
|
6879
6942
|
const files = await listMergeRequestChangedFiles(args.project_id, args.merge_request_iid, args.source_branch, args.excluded_file_patterns);
|
|
6880
6943
|
return {
|
|
6881
|
-
content: [{ type: "text", text: JSON.stringify(files
|
|
6944
|
+
content: [{ type: "text", text: JSON.stringify(files) }],
|
|
6882
6945
|
};
|
|
6883
6946
|
}
|
|
6884
6947
|
case "list_merge_request_pipelines": {
|
|
@@ -6886,35 +6949,35 @@ async function handleToolCall(params) {
|
|
|
6886
6949
|
const { project_id, merge_request_iid, ...options } = args;
|
|
6887
6950
|
const pipelines = await listMergeRequestPipelines(project_id, merge_request_iid, options);
|
|
6888
6951
|
return {
|
|
6889
|
-
content: [{ type: "text", text: JSON.stringify(pipelines
|
|
6952
|
+
content: [{ type: "text", text: JSON.stringify(pipelines) }],
|
|
6890
6953
|
};
|
|
6891
6954
|
}
|
|
6892
6955
|
case "list_merge_request_diffs": {
|
|
6893
6956
|
const args = ListMergeRequestDiffsSchema.parse(params.arguments);
|
|
6894
6957
|
const changes = await listMergeRequestDiffs(args.project_id, args.merge_request_iid, args.source_branch, args.page, args.per_page, args.unidiff);
|
|
6895
6958
|
return {
|
|
6896
|
-
content: [{ type: "text", text: JSON.stringify(changes
|
|
6959
|
+
content: [{ type: "text", text: JSON.stringify(changes) }],
|
|
6897
6960
|
};
|
|
6898
6961
|
}
|
|
6899
6962
|
case "get_merge_request_file_diff": {
|
|
6900
6963
|
const args = GetMergeRequestFileDiffSchema.parse(params.arguments);
|
|
6901
6964
|
const fileDiff = await getMergeRequestFileDiff(args.project_id, args.file_paths, args.merge_request_iid, args.source_branch, args.unidiff);
|
|
6902
6965
|
return {
|
|
6903
|
-
content: [{ type: "text", text: JSON.stringify(fileDiff
|
|
6966
|
+
content: [{ type: "text", text: JSON.stringify(fileDiff) }],
|
|
6904
6967
|
};
|
|
6905
6968
|
}
|
|
6906
6969
|
case "list_merge_request_versions": {
|
|
6907
6970
|
const args = ListMergeRequestVersionsSchema.parse(params.arguments);
|
|
6908
6971
|
const versions = await listMergeRequestVersions(args.project_id, args.merge_request_iid);
|
|
6909
6972
|
return {
|
|
6910
|
-
content: [{ type: "text", text: JSON.stringify(versions
|
|
6973
|
+
content: [{ type: "text", text: JSON.stringify(versions) }],
|
|
6911
6974
|
};
|
|
6912
6975
|
}
|
|
6913
6976
|
case "get_merge_request_version": {
|
|
6914
6977
|
const args = GetMergeRequestVersionSchema.parse(params.arguments);
|
|
6915
6978
|
const version = await getMergeRequestVersion(args.project_id, args.merge_request_iid, args.version_id, args.unidiff);
|
|
6916
6979
|
return {
|
|
6917
|
-
content: [{ type: "text", text: JSON.stringify(version
|
|
6980
|
+
content: [{ type: "text", text: JSON.stringify(version) }],
|
|
6918
6981
|
};
|
|
6919
6982
|
}
|
|
6920
6983
|
case "update_merge_request": {
|
|
@@ -6922,7 +6985,7 @@ async function handleToolCall(params) {
|
|
|
6922
6985
|
const { project_id, merge_request_iid, source_branch, ...options } = args;
|
|
6923
6986
|
const mergeRequest = await updateMergeRequest(project_id, options, merge_request_iid, source_branch);
|
|
6924
6987
|
return {
|
|
6925
|
-
content: [{ type: "text", text: JSON.stringify(mergeRequest
|
|
6988
|
+
content: [{ type: "text", text: JSON.stringify(mergeRequest) }],
|
|
6926
6989
|
};
|
|
6927
6990
|
}
|
|
6928
6991
|
case "merge_merge_request": {
|
|
@@ -6930,35 +6993,35 @@ async function handleToolCall(params) {
|
|
|
6930
6993
|
const { project_id, merge_request_iid, ...options } = args;
|
|
6931
6994
|
const mergeRequest = await mergeMergeRequest(project_id, options, merge_request_iid);
|
|
6932
6995
|
return {
|
|
6933
|
-
content: [{ type: "text", text: JSON.stringify(mergeRequest
|
|
6996
|
+
content: [{ type: "text", text: JSON.stringify(mergeRequest) }],
|
|
6934
6997
|
};
|
|
6935
6998
|
}
|
|
6936
6999
|
case "approve_merge_request": {
|
|
6937
7000
|
const args = ApproveMergeRequestSchema.parse(params.arguments);
|
|
6938
7001
|
const approvalState = await approveMergeRequest(args.project_id, args.merge_request_iid, args.sha, args.approval_password);
|
|
6939
7002
|
return {
|
|
6940
|
-
content: [{ type: "text", text: JSON.stringify(approvalState
|
|
7003
|
+
content: [{ type: "text", text: JSON.stringify(approvalState) }],
|
|
6941
7004
|
};
|
|
6942
7005
|
}
|
|
6943
7006
|
case "unapprove_merge_request": {
|
|
6944
7007
|
const args = UnapproveMergeRequestSchema.parse(params.arguments);
|
|
6945
7008
|
const approvalState = await unapproveMergeRequest(args.project_id, args.merge_request_iid);
|
|
6946
7009
|
return {
|
|
6947
|
-
content: [{ type: "text", text: JSON.stringify(approvalState
|
|
7010
|
+
content: [{ type: "text", text: JSON.stringify(approvalState) }],
|
|
6948
7011
|
};
|
|
6949
7012
|
}
|
|
6950
7013
|
case "get_merge_request_approval_state": {
|
|
6951
7014
|
const args = GetMergeRequestApprovalStateSchema.parse(params.arguments);
|
|
6952
7015
|
const approvalState = await getMergeRequestApprovalState(args.project_id, args.merge_request_iid);
|
|
6953
7016
|
return {
|
|
6954
|
-
content: [{ type: "text", text: JSON.stringify(approvalState
|
|
7017
|
+
content: [{ type: "text", text: JSON.stringify(approvalState) }],
|
|
6955
7018
|
};
|
|
6956
7019
|
}
|
|
6957
7020
|
case "get_merge_request_conflicts": {
|
|
6958
7021
|
const args = GetMergeRequestConflictsSchema.parse(params.arguments);
|
|
6959
7022
|
const conflicts = await getMergeRequestConflicts(args.project_id, args.merge_request_iid);
|
|
6960
7023
|
return {
|
|
6961
|
-
content: [{ type: "text", text: JSON.stringify(conflicts
|
|
7024
|
+
content: [{ type: "text", text: JSON.stringify(conflicts) }],
|
|
6962
7025
|
};
|
|
6963
7026
|
}
|
|
6964
7027
|
case "mr_discussions": {
|
|
@@ -6966,7 +7029,7 @@ async function handleToolCall(params) {
|
|
|
6966
7029
|
const { project_id, merge_request_iid, ...options } = args;
|
|
6967
7030
|
const discussions = await listMergeRequestDiscussions(project_id, merge_request_iid, options);
|
|
6968
7031
|
return {
|
|
6969
|
-
content: [{ type: "text", text: JSON.stringify(discussions
|
|
7032
|
+
content: [{ type: "text", text: JSON.stringify(discussions) }],
|
|
6970
7033
|
};
|
|
6971
7034
|
}
|
|
6972
7035
|
case "list_namespaces": {
|
|
@@ -6991,7 +7054,7 @@ async function handleToolCall(params) {
|
|
|
6991
7054
|
const data = await response.json();
|
|
6992
7055
|
const namespaces = z.array(GitLabNamespaceSchema).parse(data);
|
|
6993
7056
|
return {
|
|
6994
|
-
content: [{ type: "text", text: JSON.stringify(namespaces
|
|
7057
|
+
content: [{ type: "text", text: JSON.stringify(namespaces) }],
|
|
6995
7058
|
};
|
|
6996
7059
|
}
|
|
6997
7060
|
case "get_namespace": {
|
|
@@ -7004,7 +7067,7 @@ async function handleToolCall(params) {
|
|
|
7004
7067
|
const data = await response.json();
|
|
7005
7068
|
const namespace = GitLabNamespaceSchema.parse(data);
|
|
7006
7069
|
return {
|
|
7007
|
-
content: [{ type: "text", text: JSON.stringify(namespace
|
|
7070
|
+
content: [{ type: "text", text: JSON.stringify(namespace) }],
|
|
7008
7071
|
};
|
|
7009
7072
|
}
|
|
7010
7073
|
case "verify_namespace": {
|
|
@@ -7019,7 +7082,7 @@ async function handleToolCall(params) {
|
|
|
7019
7082
|
const data = await response.json();
|
|
7020
7083
|
const namespaceExists = GitLabNamespaceExistsResponseSchema.parse(data);
|
|
7021
7084
|
return {
|
|
7022
|
-
content: [{ type: "text", text: JSON.stringify(namespaceExists
|
|
7085
|
+
content: [{ type: "text", text: JSON.stringify(namespaceExists) }],
|
|
7023
7086
|
};
|
|
7024
7087
|
}
|
|
7025
7088
|
case "get_project": {
|
|
@@ -7040,14 +7103,29 @@ async function handleToolCall(params) {
|
|
|
7040
7103
|
const data = await response.json();
|
|
7041
7104
|
// Return raw data without parsing through our schema to avoid type mismatches in tests
|
|
7042
7105
|
return {
|
|
7043
|
-
content: [{ type: "text", text: JSON.stringify(data
|
|
7106
|
+
content: [{ type: "text", text: JSON.stringify(data) }],
|
|
7044
7107
|
};
|
|
7045
7108
|
}
|
|
7046
7109
|
case "list_projects": {
|
|
7047
7110
|
const args = ListProjectsSchema.parse(params.arguments);
|
|
7048
7111
|
const projects = await listProjects(args);
|
|
7049
7112
|
return {
|
|
7050
|
-
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) }],
|
|
7051
7129
|
};
|
|
7052
7130
|
}
|
|
7053
7131
|
case "list_project_members": {
|
|
@@ -7055,14 +7133,14 @@ async function handleToolCall(params) {
|
|
|
7055
7133
|
const { project_id, ...options } = args;
|
|
7056
7134
|
const members = await listProjectMembers(project_id, options);
|
|
7057
7135
|
return {
|
|
7058
|
-
content: [{ type: "text", text: JSON.stringify(members
|
|
7136
|
+
content: [{ type: "text", text: JSON.stringify(members) }],
|
|
7059
7137
|
};
|
|
7060
7138
|
}
|
|
7061
7139
|
case "get_users": {
|
|
7062
7140
|
const args = GetUsersSchema.parse(params.arguments);
|
|
7063
7141
|
const usersMap = await getUsers(args.usernames);
|
|
7064
7142
|
return {
|
|
7065
|
-
content: [{ type: "text", text: JSON.stringify(usersMap
|
|
7143
|
+
content: [{ type: "text", text: JSON.stringify(usersMap) }],
|
|
7066
7144
|
};
|
|
7067
7145
|
}
|
|
7068
7146
|
case "get_user": {
|
|
@@ -7075,7 +7153,7 @@ async function handleToolCall(params) {
|
|
|
7075
7153
|
const data = await response.json();
|
|
7076
7154
|
const user = GitLabUserFullSchema.parse(data);
|
|
7077
7155
|
return {
|
|
7078
|
-
content: [{ type: "text", text: JSON.stringify(user
|
|
7156
|
+
content: [{ type: "text", text: JSON.stringify(user) }],
|
|
7079
7157
|
};
|
|
7080
7158
|
}
|
|
7081
7159
|
case "whoami": {
|
|
@@ -7088,7 +7166,7 @@ async function handleToolCall(params) {
|
|
|
7088
7166
|
const data = await response.json();
|
|
7089
7167
|
const user = GitLabCurrentUserSchema.parse(data);
|
|
7090
7168
|
return {
|
|
7091
|
-
content: [{ type: "text", text: JSON.stringify(user
|
|
7169
|
+
content: [{ type: "text", text: JSON.stringify(user) }],
|
|
7092
7170
|
};
|
|
7093
7171
|
}
|
|
7094
7172
|
case "create_note": {
|
|
@@ -7096,7 +7174,7 @@ async function handleToolCall(params) {
|
|
|
7096
7174
|
const { project_id, noteable_type, noteable_iid, body } = args;
|
|
7097
7175
|
const note = await createNote(project_id, noteable_type, noteable_iid, body);
|
|
7098
7176
|
return {
|
|
7099
|
-
content: [{ type: "text", text: JSON.stringify(note
|
|
7177
|
+
content: [{ type: "text", text: JSON.stringify(note) }],
|
|
7100
7178
|
};
|
|
7101
7179
|
}
|
|
7102
7180
|
case "get_draft_note": {
|
|
@@ -7104,7 +7182,7 @@ async function handleToolCall(params) {
|
|
|
7104
7182
|
const { project_id, merge_request_iid, draft_note_id } = args;
|
|
7105
7183
|
const draftNote = await getDraftNote(project_id, merge_request_iid, draft_note_id);
|
|
7106
7184
|
return {
|
|
7107
|
-
content: [{ type: "text", text: JSON.stringify(draftNote
|
|
7185
|
+
content: [{ type: "text", text: JSON.stringify(draftNote) }],
|
|
7108
7186
|
};
|
|
7109
7187
|
}
|
|
7110
7188
|
case "list_draft_notes": {
|
|
@@ -7112,15 +7190,15 @@ async function handleToolCall(params) {
|
|
|
7112
7190
|
const { project_id, merge_request_iid } = args;
|
|
7113
7191
|
const draftNotes = await listDraftNotes(project_id, merge_request_iid);
|
|
7114
7192
|
return {
|
|
7115
|
-
content: [{ type: "text", text: JSON.stringify(draftNotes
|
|
7193
|
+
content: [{ type: "text", text: JSON.stringify(draftNotes) }],
|
|
7116
7194
|
};
|
|
7117
7195
|
}
|
|
7118
7196
|
case "create_draft_note": {
|
|
7119
7197
|
const args = CreateDraftNoteSchema.parse(params.arguments);
|
|
7120
|
-
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;
|
|
7121
7199
|
const draftNote = await createDraftNote(project_id, merge_request_iid, body, in_reply_to_discussion_id, position, resolve_discussion);
|
|
7122
7200
|
return {
|
|
7123
|
-
content: [{ type: "text", text: JSON.stringify(draftNote
|
|
7201
|
+
content: [{ type: "text", text: JSON.stringify(draftNote) }],
|
|
7124
7202
|
};
|
|
7125
7203
|
}
|
|
7126
7204
|
case "update_draft_note": {
|
|
@@ -7128,7 +7206,7 @@ async function handleToolCall(params) {
|
|
|
7128
7206
|
const { project_id, merge_request_iid, draft_note_id, body, position, resolve_discussion } = args;
|
|
7129
7207
|
const draftNote = await updateDraftNote(project_id, merge_request_iid, draft_note_id, body, position, resolve_discussion);
|
|
7130
7208
|
return {
|
|
7131
|
-
content: [{ type: "text", text: JSON.stringify(draftNote
|
|
7209
|
+
content: [{ type: "text", text: JSON.stringify(draftNote) }],
|
|
7132
7210
|
};
|
|
7133
7211
|
}
|
|
7134
7212
|
case "delete_draft_note": {
|
|
@@ -7144,7 +7222,7 @@ async function handleToolCall(params) {
|
|
|
7144
7222
|
const { project_id, merge_request_iid, draft_note_id } = args;
|
|
7145
7223
|
const publishedNote = await publishDraftNote(project_id, merge_request_iid, draft_note_id);
|
|
7146
7224
|
return {
|
|
7147
|
-
content: [{ type: "text", text: JSON.stringify(publishedNote
|
|
7225
|
+
content: [{ type: "text", text: JSON.stringify(publishedNote) }],
|
|
7148
7226
|
};
|
|
7149
7227
|
}
|
|
7150
7228
|
case "bulk_publish_draft_notes": {
|
|
@@ -7152,7 +7230,7 @@ async function handleToolCall(params) {
|
|
|
7152
7230
|
const { project_id, merge_request_iid } = args;
|
|
7153
7231
|
const publishedNotes = await bulkPublishDraftNotes(project_id, merge_request_iid);
|
|
7154
7232
|
return {
|
|
7155
|
-
content: [{ type: "text", text: JSON.stringify(publishedNotes
|
|
7233
|
+
content: [{ type: "text", text: JSON.stringify(publishedNotes) }],
|
|
7156
7234
|
};
|
|
7157
7235
|
}
|
|
7158
7236
|
case "create_merge_request_thread": {
|
|
@@ -7160,7 +7238,7 @@ async function handleToolCall(params) {
|
|
|
7160
7238
|
const { project_id, merge_request_iid, body, position, created_at } = args;
|
|
7161
7239
|
const thread = await createMergeRequestThread(project_id, merge_request_iid, body, position, created_at);
|
|
7162
7240
|
return {
|
|
7163
|
-
content: [{ type: "text", text: JSON.stringify(thread
|
|
7241
|
+
content: [{ type: "text", text: JSON.stringify(thread) }],
|
|
7164
7242
|
};
|
|
7165
7243
|
}
|
|
7166
7244
|
case "resolve_merge_request_thread": {
|
|
@@ -7177,21 +7255,21 @@ async function handleToolCall(params) {
|
|
|
7177
7255
|
const cleanedOptions = cleanMutuallyExclusiveIdUsernameOptions(options);
|
|
7178
7256
|
const issues = await listIssues(project_id, cleanedOptions);
|
|
7179
7257
|
return {
|
|
7180
|
-
content: [{ type: "text", text: JSON.stringify(issues
|
|
7258
|
+
content: [{ type: "text", text: JSON.stringify(issues) }],
|
|
7181
7259
|
};
|
|
7182
7260
|
}
|
|
7183
7261
|
case "my_issues": {
|
|
7184
7262
|
const args = MyIssuesSchema.parse(params.arguments);
|
|
7185
7263
|
const issues = await myIssues(args);
|
|
7186
7264
|
return {
|
|
7187
|
-
content: [{ type: "text", text: JSON.stringify(issues
|
|
7265
|
+
content: [{ type: "text", text: JSON.stringify(issues) }],
|
|
7188
7266
|
};
|
|
7189
7267
|
}
|
|
7190
7268
|
case "get_issue": {
|
|
7191
7269
|
const args = GetIssueSchema.parse(params.arguments);
|
|
7192
7270
|
const issue = await getIssue(args.project_id, args.issue_iid);
|
|
7193
7271
|
return {
|
|
7194
|
-
content: [{ type: "text", text: JSON.stringify(issue
|
|
7272
|
+
content: [{ type: "text", text: JSON.stringify(issue) }],
|
|
7195
7273
|
};
|
|
7196
7274
|
}
|
|
7197
7275
|
case "update_issue": {
|
|
@@ -7199,7 +7277,7 @@ async function handleToolCall(params) {
|
|
|
7199
7277
|
const { project_id, issue_iid, ...options } = args;
|
|
7200
7278
|
const issue = await updateIssue(project_id, issue_iid, options);
|
|
7201
7279
|
return {
|
|
7202
|
-
content: [{ type: "text", text: JSON.stringify(issue
|
|
7280
|
+
content: [{ type: "text", text: JSON.stringify(issue) }],
|
|
7203
7281
|
};
|
|
7204
7282
|
}
|
|
7205
7283
|
case "update_issue_description_patch": {
|
|
@@ -7293,7 +7371,7 @@ async function handleToolCall(params) {
|
|
|
7293
7371
|
const args = ListIssueLinksSchema.parse(params.arguments);
|
|
7294
7372
|
const links = await listIssueLinks(args.project_id, args.issue_iid);
|
|
7295
7373
|
return {
|
|
7296
|
-
content: [{ type: "text", text: JSON.stringify(links
|
|
7374
|
+
content: [{ type: "text", text: JSON.stringify(links) }],
|
|
7297
7375
|
};
|
|
7298
7376
|
}
|
|
7299
7377
|
case "list_issue_discussions": {
|
|
@@ -7301,21 +7379,21 @@ async function handleToolCall(params) {
|
|
|
7301
7379
|
const { project_id, issue_iid, ...options } = args;
|
|
7302
7380
|
const discussions = await listIssueDiscussions(project_id, issue_iid, options);
|
|
7303
7381
|
return {
|
|
7304
|
-
content: [{ type: "text", text: JSON.stringify(discussions
|
|
7382
|
+
content: [{ type: "text", text: JSON.stringify(discussions) }],
|
|
7305
7383
|
};
|
|
7306
7384
|
}
|
|
7307
7385
|
case "get_issue_link": {
|
|
7308
7386
|
const args = GetIssueLinkSchema.parse(params.arguments);
|
|
7309
7387
|
const link = await getIssueLink(args.project_id, args.issue_iid, args.issue_link_id);
|
|
7310
7388
|
return {
|
|
7311
|
-
content: [{ type: "text", text: JSON.stringify(link
|
|
7389
|
+
content: [{ type: "text", text: JSON.stringify(link) }],
|
|
7312
7390
|
};
|
|
7313
7391
|
}
|
|
7314
7392
|
case "create_issue_link": {
|
|
7315
7393
|
const args = CreateIssueLinkSchema.parse(params.arguments);
|
|
7316
7394
|
const link = await createIssueLink(args.project_id, args.issue_iid, args.target_project_id, args.target_issue_iid, args.link_type);
|
|
7317
7395
|
return {
|
|
7318
|
-
content: [{ type: "text", text: JSON.stringify(link
|
|
7396
|
+
content: [{ type: "text", text: JSON.stringify(link) }],
|
|
7319
7397
|
};
|
|
7320
7398
|
}
|
|
7321
7399
|
case "delete_issue_link": {
|
|
@@ -7337,7 +7415,7 @@ async function handleToolCall(params) {
|
|
|
7337
7415
|
const args = GetWorkItemSchema.parse(params.arguments);
|
|
7338
7416
|
const result = await getWorkItem(args.project_id, args.iid);
|
|
7339
7417
|
return {
|
|
7340
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
7418
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
7341
7419
|
};
|
|
7342
7420
|
}
|
|
7343
7421
|
case "list_work_items": {
|
|
@@ -7345,7 +7423,7 @@ async function handleToolCall(params) {
|
|
|
7345
7423
|
const { project_id, ...options } = args;
|
|
7346
7424
|
const result = await listWorkItems(project_id, options);
|
|
7347
7425
|
return {
|
|
7348
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
7426
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
7349
7427
|
};
|
|
7350
7428
|
}
|
|
7351
7429
|
case "create_work_item": {
|
|
@@ -7353,7 +7431,7 @@ async function handleToolCall(params) {
|
|
|
7353
7431
|
const { project_id, ...options } = args;
|
|
7354
7432
|
const result = await createWorkItem(project_id, options);
|
|
7355
7433
|
return {
|
|
7356
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
7434
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
7357
7435
|
};
|
|
7358
7436
|
}
|
|
7359
7437
|
case "update_work_item": {
|
|
@@ -7361,117 +7439,117 @@ async function handleToolCall(params) {
|
|
|
7361
7439
|
const { project_id, iid, ...options } = args;
|
|
7362
7440
|
const result = await updateWorkItem(project_id, iid, options);
|
|
7363
7441
|
return {
|
|
7364
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
7442
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
7365
7443
|
};
|
|
7366
7444
|
}
|
|
7367
7445
|
case "convert_work_item_type": {
|
|
7368
7446
|
const args = ConvertWorkItemTypeSchema.parse(params.arguments);
|
|
7369
7447
|
const result = await convertIssueType(args.project_id, args.iid, args.new_type);
|
|
7370
7448
|
return {
|
|
7371
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
7449
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
7372
7450
|
};
|
|
7373
7451
|
}
|
|
7374
7452
|
case "list_work_item_statuses": {
|
|
7375
7453
|
const args = ListWorkItemStatusesSchema.parse(params.arguments);
|
|
7376
7454
|
const result = await listIssueStatuses(args.project_id, args.work_item_type);
|
|
7377
7455
|
return {
|
|
7378
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
7456
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
7379
7457
|
};
|
|
7380
7458
|
}
|
|
7381
7459
|
case "list_custom_field_definitions": {
|
|
7382
7460
|
const args = ListCustomFieldDefinitionsSchema.parse(params.arguments);
|
|
7383
7461
|
const result = await listCustomFieldDefinitions(args.project_id, args.work_item_type);
|
|
7384
7462
|
return {
|
|
7385
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
7463
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
7386
7464
|
};
|
|
7387
7465
|
}
|
|
7388
7466
|
case "move_work_item": {
|
|
7389
7467
|
const args = MoveWorkItemSchema.parse(params.arguments);
|
|
7390
7468
|
const result = await moveWorkItem(args.project_id, args.iid, args.target_project_id);
|
|
7391
7469
|
return {
|
|
7392
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
7470
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
7393
7471
|
};
|
|
7394
7472
|
}
|
|
7395
7473
|
case "list_work_item_notes": {
|
|
7396
7474
|
const args = ListWorkItemNotesSchema.parse(params.arguments);
|
|
7397
7475
|
const result = await listWorkItemNotes(args.project_id, args.iid, args);
|
|
7398
7476
|
return {
|
|
7399
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
7477
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
7400
7478
|
};
|
|
7401
7479
|
}
|
|
7402
7480
|
case "create_work_item_note": {
|
|
7403
7481
|
const args = CreateWorkItemNoteSchema.parse(params.arguments);
|
|
7404
7482
|
const result = await createWorkItemNote(args.project_id, args.iid, args.body, args);
|
|
7405
7483
|
return {
|
|
7406
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
7484
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
7407
7485
|
};
|
|
7408
7486
|
}
|
|
7409
7487
|
case "list_work_item_emoji_reactions": {
|
|
7410
7488
|
const args = ListWorkItemEmojiReactionsSchema.parse(params.arguments);
|
|
7411
7489
|
const { workItemGID } = await resolveWorkItemGID(args.project_id, args.iid);
|
|
7412
7490
|
const result = await listGraphQLAwardEmoji(workItemGID);
|
|
7413
|
-
return { content: [{ type: "text", text: JSON.stringify(result
|
|
7491
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
7414
7492
|
}
|
|
7415
7493
|
case "list_work_item_note_emoji_reactions": {
|
|
7416
7494
|
const args = ListWorkItemNoteEmojiReactionsSchema.parse(params.arguments);
|
|
7417
7495
|
const result = await listGraphQLAwardEmoji(args.note_id);
|
|
7418
|
-
return { content: [{ type: "text", text: JSON.stringify(result
|
|
7496
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
7419
7497
|
}
|
|
7420
7498
|
case "create_work_item_emoji_reaction": {
|
|
7421
7499
|
const args = CreateWorkItemEmojiReactionSchema.parse(params.arguments);
|
|
7422
7500
|
const { workItemGID } = await resolveWorkItemGID(args.project_id, args.iid);
|
|
7423
7501
|
const result = await addGraphQLAwardEmoji(workItemGID, args.name);
|
|
7424
|
-
return { content: [{ type: "text", text: JSON.stringify(result
|
|
7502
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
7425
7503
|
}
|
|
7426
7504
|
case "delete_work_item_emoji_reaction": {
|
|
7427
7505
|
const args = DeleteWorkItemEmojiReactionSchema.parse(params.arguments);
|
|
7428
7506
|
const { workItemGID } = await resolveWorkItemGID(args.project_id, args.iid);
|
|
7429
7507
|
const result = await removeGraphQLAwardEmoji(workItemGID, args.name);
|
|
7430
|
-
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" }) }] };
|
|
7431
7509
|
}
|
|
7432
7510
|
case "create_work_item_note_emoji_reaction": {
|
|
7433
7511
|
const args = CreateWorkItemNoteEmojiReactionSchema.parse(params.arguments);
|
|
7434
7512
|
const result = await addGraphQLAwardEmoji(args.note_id, args.name);
|
|
7435
|
-
return { content: [{ type: "text", text: JSON.stringify(result
|
|
7513
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
7436
7514
|
}
|
|
7437
7515
|
case "delete_work_item_note_emoji_reaction": {
|
|
7438
7516
|
const args = DeleteWorkItemNoteEmojiReactionSchema.parse(params.arguments);
|
|
7439
7517
|
const result = await removeGraphQLAwardEmoji(args.note_id, args.name);
|
|
7440
|
-
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" }) }] };
|
|
7441
7519
|
}
|
|
7442
7520
|
case "get_timeline_events": {
|
|
7443
7521
|
const args = GetTimelineEventsSchema.parse(params.arguments);
|
|
7444
7522
|
const result = await getTimelineEvents(args.project_id, args.incident_iid);
|
|
7445
7523
|
return {
|
|
7446
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
7524
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
7447
7525
|
};
|
|
7448
7526
|
}
|
|
7449
7527
|
case "create_timeline_event": {
|
|
7450
7528
|
const args = CreateTimelineEventSchema.parse(params.arguments);
|
|
7451
7529
|
const result = await createTimelineEvent(args.project_id, args.incident_iid, args.note, args.occurred_at, args.tag_names);
|
|
7452
7530
|
return {
|
|
7453
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
7531
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
7454
7532
|
};
|
|
7455
7533
|
}
|
|
7456
7534
|
case "list_labels": {
|
|
7457
7535
|
const args = ListLabelsSchema.parse(params.arguments);
|
|
7458
7536
|
const labels = await listLabels(args.project_id, args);
|
|
7459
7537
|
return {
|
|
7460
|
-
content: [{ type: "text", text: JSON.stringify(labels
|
|
7538
|
+
content: [{ type: "text", text: JSON.stringify(labels) }],
|
|
7461
7539
|
};
|
|
7462
7540
|
}
|
|
7463
7541
|
case "get_label": {
|
|
7464
7542
|
const args = GetLabelSchema.parse(params.arguments);
|
|
7465
7543
|
const label = await getLabel(args.project_id, args.label_id, args.include_ancestor_groups);
|
|
7466
7544
|
return {
|
|
7467
|
-
content: [{ type: "text", text: JSON.stringify(label
|
|
7545
|
+
content: [{ type: "text", text: JSON.stringify(label) }],
|
|
7468
7546
|
};
|
|
7469
7547
|
}
|
|
7470
7548
|
case "create_label": {
|
|
7471
7549
|
const args = CreateLabelSchema.parse(params.arguments);
|
|
7472
7550
|
const label = await createLabel(args.project_id, args);
|
|
7473
7551
|
return {
|
|
7474
|
-
content: [{ type: "text", text: JSON.stringify(label
|
|
7552
|
+
content: [{ type: "text", text: JSON.stringify(label) }],
|
|
7475
7553
|
};
|
|
7476
7554
|
}
|
|
7477
7555
|
case "update_label": {
|
|
@@ -7479,7 +7557,7 @@ async function handleToolCall(params) {
|
|
|
7479
7557
|
const { project_id, label_id, ...options } = args;
|
|
7480
7558
|
const label = await updateLabel(project_id, label_id, options);
|
|
7481
7559
|
return {
|
|
7482
|
-
content: [{ type: "text", text: JSON.stringify(label
|
|
7560
|
+
content: [{ type: "text", text: JSON.stringify(label) }],
|
|
7483
7561
|
};
|
|
7484
7562
|
}
|
|
7485
7563
|
case "delete_label": {
|
|
@@ -7498,7 +7576,7 @@ async function handleToolCall(params) {
|
|
|
7498
7576
|
const args = ListGroupProjectsSchema.parse(params.arguments);
|
|
7499
7577
|
const projects = await listGroupProjects(args);
|
|
7500
7578
|
return {
|
|
7501
|
-
content: [{ type: "text", text: JSON.stringify(projects
|
|
7579
|
+
content: [{ type: "text", text: JSON.stringify(projects) }],
|
|
7502
7580
|
};
|
|
7503
7581
|
}
|
|
7504
7582
|
case "list_wiki_pages": {
|
|
@@ -7509,28 +7587,28 @@ async function handleToolCall(params) {
|
|
|
7509
7587
|
with_content,
|
|
7510
7588
|
});
|
|
7511
7589
|
return {
|
|
7512
|
-
content: [{ type: "text", text: JSON.stringify(wikiPages
|
|
7590
|
+
content: [{ type: "text", text: JSON.stringify(wikiPages) }],
|
|
7513
7591
|
};
|
|
7514
7592
|
}
|
|
7515
7593
|
case "get_wiki_page": {
|
|
7516
7594
|
const { project_id, slug } = GetWikiPageSchema.parse(params.arguments);
|
|
7517
7595
|
const wikiPage = await getWikiPage(project_id, slug);
|
|
7518
7596
|
return {
|
|
7519
|
-
content: [{ type: "text", text: JSON.stringify(wikiPage
|
|
7597
|
+
content: [{ type: "text", text: JSON.stringify(wikiPage) }],
|
|
7520
7598
|
};
|
|
7521
7599
|
}
|
|
7522
7600
|
case "create_wiki_page": {
|
|
7523
7601
|
const { project_id, title, content, format } = CreateWikiPageSchema.parse(params.arguments);
|
|
7524
7602
|
const wikiPage = await createWikiPage(project_id, title, content, format);
|
|
7525
7603
|
return {
|
|
7526
|
-
content: [{ type: "text", text: JSON.stringify(wikiPage
|
|
7604
|
+
content: [{ type: "text", text: JSON.stringify(wikiPage) }],
|
|
7527
7605
|
};
|
|
7528
7606
|
}
|
|
7529
7607
|
case "update_wiki_page": {
|
|
7530
7608
|
const { project_id, slug, title, content, format } = UpdateWikiPageSchema.parse(params.arguments);
|
|
7531
7609
|
const wikiPage = await updateWikiPage(project_id, slug, title, content, format);
|
|
7532
7610
|
return {
|
|
7533
|
-
content: [{ type: "text", text: JSON.stringify(wikiPage
|
|
7611
|
+
content: [{ type: "text", text: JSON.stringify(wikiPage) }],
|
|
7534
7612
|
};
|
|
7535
7613
|
}
|
|
7536
7614
|
case "delete_wiki_page": {
|
|
@@ -7556,28 +7634,28 @@ async function handleToolCall(params) {
|
|
|
7556
7634
|
with_content,
|
|
7557
7635
|
});
|
|
7558
7636
|
return {
|
|
7559
|
-
content: [{ type: "text", text: JSON.stringify(wikiPages
|
|
7637
|
+
content: [{ type: "text", text: JSON.stringify(wikiPages) }],
|
|
7560
7638
|
};
|
|
7561
7639
|
}
|
|
7562
7640
|
case "get_group_wiki_page": {
|
|
7563
7641
|
const { group_id, slug } = GetGroupWikiPageSchema.parse(params.arguments);
|
|
7564
7642
|
const wikiPage = await getGroupWikiPage(group_id, slug);
|
|
7565
7643
|
return {
|
|
7566
|
-
content: [{ type: "text", text: JSON.stringify(wikiPage
|
|
7644
|
+
content: [{ type: "text", text: JSON.stringify(wikiPage) }],
|
|
7567
7645
|
};
|
|
7568
7646
|
}
|
|
7569
7647
|
case "create_group_wiki_page": {
|
|
7570
7648
|
const { group_id, title, content, format } = CreateGroupWikiPageSchema.parse(params.arguments);
|
|
7571
7649
|
const wikiPage = await createGroupWikiPage(group_id, title, content, format);
|
|
7572
7650
|
return {
|
|
7573
|
-
content: [{ type: "text", text: JSON.stringify(wikiPage
|
|
7651
|
+
content: [{ type: "text", text: JSON.stringify(wikiPage) }],
|
|
7574
7652
|
};
|
|
7575
7653
|
}
|
|
7576
7654
|
case "update_group_wiki_page": {
|
|
7577
7655
|
const { group_id, slug, title, content, format } = UpdateGroupWikiPageSchema.parse(params.arguments);
|
|
7578
7656
|
const wikiPage = await updateGroupWikiPage(group_id, slug, title, content, format);
|
|
7579
7657
|
return {
|
|
7580
|
-
content: [{ type: "text", text: JSON.stringify(wikiPage
|
|
7658
|
+
content: [{ type: "text", text: JSON.stringify(wikiPage) }],
|
|
7581
7659
|
};
|
|
7582
7660
|
}
|
|
7583
7661
|
case "delete_group_wiki_page": {
|
|
@@ -7608,7 +7686,7 @@ async function handleToolCall(params) {
|
|
|
7608
7686
|
}
|
|
7609
7687
|
: items;
|
|
7610
7688
|
return {
|
|
7611
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
7689
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
7612
7690
|
};
|
|
7613
7691
|
}
|
|
7614
7692
|
case "list_pipelines": {
|
|
@@ -7616,7 +7694,7 @@ async function handleToolCall(params) {
|
|
|
7616
7694
|
const { project_id, ...options } = args;
|
|
7617
7695
|
const pipelines = await listPipelines(project_id, options);
|
|
7618
7696
|
return {
|
|
7619
|
-
content: [{ type: "text", text: JSON.stringify(pipelines
|
|
7697
|
+
content: [{ type: "text", text: JSON.stringify(pipelines) }],
|
|
7620
7698
|
};
|
|
7621
7699
|
}
|
|
7622
7700
|
case "get_pipeline": {
|
|
@@ -7626,7 +7704,7 @@ async function handleToolCall(params) {
|
|
|
7626
7704
|
content: [
|
|
7627
7705
|
{
|
|
7628
7706
|
type: "text",
|
|
7629
|
-
text: JSON.stringify(pipeline
|
|
7707
|
+
text: JSON.stringify(pipeline),
|
|
7630
7708
|
},
|
|
7631
7709
|
],
|
|
7632
7710
|
};
|
|
@@ -7636,14 +7714,14 @@ async function handleToolCall(params) {
|
|
|
7636
7714
|
const { project_id, ...options } = args;
|
|
7637
7715
|
const deployments = await listDeployments(project_id, options);
|
|
7638
7716
|
return {
|
|
7639
|
-
content: [{ type: "text", text: JSON.stringify(deployments
|
|
7717
|
+
content: [{ type: "text", text: JSON.stringify(deployments) }],
|
|
7640
7718
|
};
|
|
7641
7719
|
}
|
|
7642
7720
|
case "get_deployment": {
|
|
7643
7721
|
const { project_id, deployment_id } = GetDeploymentSchema.parse(params.arguments);
|
|
7644
7722
|
const deployment = await getDeployment(project_id, deployment_id);
|
|
7645
7723
|
return {
|
|
7646
|
-
content: [{ type: "text", text: JSON.stringify(deployment
|
|
7724
|
+
content: [{ type: "text", text: JSON.stringify(deployment) }],
|
|
7647
7725
|
};
|
|
7648
7726
|
}
|
|
7649
7727
|
case "list_environments": {
|
|
@@ -7651,14 +7729,14 @@ async function handleToolCall(params) {
|
|
|
7651
7729
|
const { project_id, ...options } = args;
|
|
7652
7730
|
const environments = await listEnvironments(project_id, options);
|
|
7653
7731
|
return {
|
|
7654
|
-
content: [{ type: "text", text: JSON.stringify(environments
|
|
7732
|
+
content: [{ type: "text", text: JSON.stringify(environments) }],
|
|
7655
7733
|
};
|
|
7656
7734
|
}
|
|
7657
7735
|
case "get_environment": {
|
|
7658
7736
|
const { project_id, environment_id } = GetEnvironmentSchema.parse(params.arguments);
|
|
7659
7737
|
const environment = await getEnvironment(project_id, environment_id);
|
|
7660
7738
|
return {
|
|
7661
|
-
content: [{ type: "text", text: JSON.stringify(environment
|
|
7739
|
+
content: [{ type: "text", text: JSON.stringify(environment) }],
|
|
7662
7740
|
};
|
|
7663
7741
|
}
|
|
7664
7742
|
case "list_pipeline_jobs": {
|
|
@@ -7668,7 +7746,7 @@ async function handleToolCall(params) {
|
|
|
7668
7746
|
content: [
|
|
7669
7747
|
{
|
|
7670
7748
|
type: "text",
|
|
7671
|
-
text: JSON.stringify(jobs
|
|
7749
|
+
text: JSON.stringify(jobs),
|
|
7672
7750
|
},
|
|
7673
7751
|
],
|
|
7674
7752
|
};
|
|
@@ -7680,7 +7758,7 @@ async function handleToolCall(params) {
|
|
|
7680
7758
|
content: [
|
|
7681
7759
|
{
|
|
7682
7760
|
type: "text",
|
|
7683
|
-
text: JSON.stringify(triggerJobs
|
|
7761
|
+
text: JSON.stringify(triggerJobs),
|
|
7684
7762
|
},
|
|
7685
7763
|
],
|
|
7686
7764
|
};
|
|
@@ -7692,7 +7770,7 @@ async function handleToolCall(params) {
|
|
|
7692
7770
|
content: [
|
|
7693
7771
|
{
|
|
7694
7772
|
type: "text",
|
|
7695
|
-
text: JSON.stringify(jobDetails
|
|
7773
|
+
text: JSON.stringify(jobDetails),
|
|
7696
7774
|
},
|
|
7697
7775
|
],
|
|
7698
7776
|
};
|
|
@@ -7714,7 +7792,7 @@ async function handleToolCall(params) {
|
|
|
7714
7792
|
const { project_id, ...options } = args;
|
|
7715
7793
|
const result = await validateCiLint(project_id, options);
|
|
7716
7794
|
return {
|
|
7717
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
7795
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
7718
7796
|
};
|
|
7719
7797
|
}
|
|
7720
7798
|
case "validate_project_ci_lint": {
|
|
@@ -7722,8 +7800,130 @@ async function handleToolCall(params) {
|
|
|
7722
7800
|
const { project_id, ...options } = args;
|
|
7723
7801
|
const result = await validateProjectCiLint(project_id, options);
|
|
7724
7802
|
return {
|
|
7725
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
7726
|
-
};
|
|
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) }] };
|
|
7727
7927
|
}
|
|
7728
7928
|
case "create_pipeline": {
|
|
7729
7929
|
const { project_id, ref, variables, inputs } = CreatePipelineSchema.parse(params.arguments);
|
|
@@ -7804,7 +8004,7 @@ async function handleToolCall(params) {
|
|
|
7804
8004
|
content: [
|
|
7805
8005
|
{
|
|
7806
8006
|
type: "text",
|
|
7807
|
-
text: JSON.stringify(artifacts
|
|
8007
|
+
text: JSON.stringify(artifacts),
|
|
7808
8008
|
},
|
|
7809
8009
|
],
|
|
7810
8010
|
};
|
|
@@ -7817,7 +8017,7 @@ async function handleToolCall(params) {
|
|
|
7817
8017
|
}
|
|
7818
8018
|
const downloadUrl = buildDownloadUrl("job-artifacts", { project_id, job_id });
|
|
7819
8019
|
return {
|
|
7820
|
-
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` }) }],
|
|
7821
8021
|
};
|
|
7822
8022
|
}
|
|
7823
8023
|
const filePath = await downloadJobArtifacts(project_id, job_id, local_path);
|
|
@@ -7825,7 +8025,7 @@ async function handleToolCall(params) {
|
|
|
7825
8025
|
content: [
|
|
7826
8026
|
{
|
|
7827
8027
|
type: "text",
|
|
7828
|
-
text: JSON.stringify({ success: true, file_path: filePath }
|
|
8028
|
+
text: JSON.stringify({ success: true, file_path: filePath }),
|
|
7829
8029
|
},
|
|
7830
8030
|
],
|
|
7831
8031
|
};
|
|
@@ -7847,7 +8047,7 @@ async function handleToolCall(params) {
|
|
|
7847
8047
|
const cleanedOptions = cleanMutuallyExclusiveIdUsernameOptions(options, LIST_MERGE_REQUESTS_ID_USERNAME_PAIRS);
|
|
7848
8048
|
const mergeRequests = await listMergeRequests(project_id, cleanedOptions);
|
|
7849
8049
|
return {
|
|
7850
|
-
content: [{ type: "text", text: JSON.stringify(mergeRequests
|
|
8050
|
+
content: [{ type: "text", text: JSON.stringify(mergeRequests) }],
|
|
7851
8051
|
};
|
|
7852
8052
|
}
|
|
7853
8053
|
case "list_milestones": {
|
|
@@ -7857,7 +8057,7 @@ async function handleToolCall(params) {
|
|
|
7857
8057
|
content: [
|
|
7858
8058
|
{
|
|
7859
8059
|
type: "text",
|
|
7860
|
-
text: JSON.stringify(milestones
|
|
8060
|
+
text: JSON.stringify(milestones),
|
|
7861
8061
|
},
|
|
7862
8062
|
],
|
|
7863
8063
|
};
|
|
@@ -7869,7 +8069,7 @@ async function handleToolCall(params) {
|
|
|
7869
8069
|
content: [
|
|
7870
8070
|
{
|
|
7871
8071
|
type: "text",
|
|
7872
|
-
text: JSON.stringify(milestone
|
|
8072
|
+
text: JSON.stringify(milestone),
|
|
7873
8073
|
},
|
|
7874
8074
|
],
|
|
7875
8075
|
};
|
|
@@ -7881,7 +8081,7 @@ async function handleToolCall(params) {
|
|
|
7881
8081
|
content: [
|
|
7882
8082
|
{
|
|
7883
8083
|
type: "text",
|
|
7884
|
-
text: JSON.stringify(milestone
|
|
8084
|
+
text: JSON.stringify(milestone),
|
|
7885
8085
|
},
|
|
7886
8086
|
],
|
|
7887
8087
|
};
|
|
@@ -7893,7 +8093,7 @@ async function handleToolCall(params) {
|
|
|
7893
8093
|
content: [
|
|
7894
8094
|
{
|
|
7895
8095
|
type: "text",
|
|
7896
|
-
text: JSON.stringify(milestone
|
|
8096
|
+
text: JSON.stringify(milestone),
|
|
7897
8097
|
},
|
|
7898
8098
|
],
|
|
7899
8099
|
};
|
|
@@ -7920,7 +8120,7 @@ async function handleToolCall(params) {
|
|
|
7920
8120
|
content: [
|
|
7921
8121
|
{
|
|
7922
8122
|
type: "text",
|
|
7923
|
-
text: JSON.stringify(issues
|
|
8123
|
+
text: JSON.stringify(issues),
|
|
7924
8124
|
},
|
|
7925
8125
|
],
|
|
7926
8126
|
};
|
|
@@ -7932,7 +8132,7 @@ async function handleToolCall(params) {
|
|
|
7932
8132
|
content: [
|
|
7933
8133
|
{
|
|
7934
8134
|
type: "text",
|
|
7935
|
-
text: JSON.stringify(mergeRequests
|
|
8135
|
+
text: JSON.stringify(mergeRequests),
|
|
7936
8136
|
},
|
|
7937
8137
|
],
|
|
7938
8138
|
};
|
|
@@ -7944,7 +8144,7 @@ async function handleToolCall(params) {
|
|
|
7944
8144
|
content: [
|
|
7945
8145
|
{
|
|
7946
8146
|
type: "text",
|
|
7947
|
-
text: JSON.stringify(milestone
|
|
8147
|
+
text: JSON.stringify(milestone),
|
|
7948
8148
|
},
|
|
7949
8149
|
],
|
|
7950
8150
|
};
|
|
@@ -7956,7 +8156,7 @@ async function handleToolCall(params) {
|
|
|
7956
8156
|
content: [
|
|
7957
8157
|
{
|
|
7958
8158
|
type: "text",
|
|
7959
|
-
text: JSON.stringify(events
|
|
8159
|
+
text: JSON.stringify(events),
|
|
7960
8160
|
},
|
|
7961
8161
|
],
|
|
7962
8162
|
};
|
|
@@ -7965,21 +8165,21 @@ async function handleToolCall(params) {
|
|
|
7965
8165
|
const args = ListCommitsSchema.parse(params.arguments);
|
|
7966
8166
|
const commits = await listCommits(args.project_id, args);
|
|
7967
8167
|
return {
|
|
7968
|
-
content: [{ type: "text", text: JSON.stringify(commits
|
|
8168
|
+
content: [{ type: "text", text: JSON.stringify(commits) }],
|
|
7969
8169
|
};
|
|
7970
8170
|
}
|
|
7971
8171
|
case "get_commit": {
|
|
7972
8172
|
const args = GetCommitSchema.parse(params.arguments);
|
|
7973
8173
|
const commit = await getCommit(args.project_id, args.sha, args.stats);
|
|
7974
8174
|
return {
|
|
7975
|
-
content: [{ type: "text", text: JSON.stringify(commit
|
|
8175
|
+
content: [{ type: "text", text: JSON.stringify(commit) }],
|
|
7976
8176
|
};
|
|
7977
8177
|
}
|
|
7978
8178
|
case "get_commit_diff": {
|
|
7979
8179
|
const args = GetCommitDiffSchema.parse(params.arguments);
|
|
7980
8180
|
const diff = await getCommitDiff(args.project_id, args.sha, args.full_diff);
|
|
7981
8181
|
return {
|
|
7982
|
-
content: [{ type: "text", text: JSON.stringify(diff
|
|
8182
|
+
content: [{ type: "text", text: JSON.stringify(diff) }],
|
|
7983
8183
|
};
|
|
7984
8184
|
}
|
|
7985
8185
|
case "get_file_blame": {
|
|
@@ -7987,7 +8187,7 @@ async function handleToolCall(params) {
|
|
|
7987
8187
|
const { project_id, ...options } = args;
|
|
7988
8188
|
const blame = await getFileBlame(project_id, options);
|
|
7989
8189
|
return {
|
|
7990
|
-
content: [{ type: "text", text: JSON.stringify(blame
|
|
8190
|
+
content: [{ type: "text", text: JSON.stringify(blame) }],
|
|
7991
8191
|
};
|
|
7992
8192
|
}
|
|
7993
8193
|
case "list_commit_statuses": {
|
|
@@ -7995,7 +8195,7 @@ async function handleToolCall(params) {
|
|
|
7995
8195
|
const { project_id, sha, ...options } = args;
|
|
7996
8196
|
const statuses = await listCommitStatuses(project_id, sha, options);
|
|
7997
8197
|
return {
|
|
7998
|
-
content: [{ type: "text", text: JSON.stringify(statuses
|
|
8198
|
+
content: [{ type: "text", text: JSON.stringify(statuses) }],
|
|
7999
8199
|
};
|
|
8000
8200
|
}
|
|
8001
8201
|
case "create_commit_status": {
|
|
@@ -8003,14 +8203,14 @@ async function handleToolCall(params) {
|
|
|
8003
8203
|
const { project_id, sha, ...options } = args;
|
|
8004
8204
|
const status = await createCommitStatus(project_id, sha, options);
|
|
8005
8205
|
return {
|
|
8006
|
-
content: [{ type: "text", text: JSON.stringify(status
|
|
8206
|
+
content: [{ type: "text", text: JSON.stringify(status) }],
|
|
8007
8207
|
};
|
|
8008
8208
|
}
|
|
8009
8209
|
case "list_group_iterations": {
|
|
8010
8210
|
const args = ListGroupIterationsSchema.parse(params.arguments);
|
|
8011
8211
|
const iterations = await listGroupIterations(args.group_id, args);
|
|
8012
8212
|
return {
|
|
8013
|
-
content: [{ type: "text", text: JSON.stringify(iterations
|
|
8213
|
+
content: [{ type: "text", text: JSON.stringify(iterations) }],
|
|
8014
8214
|
};
|
|
8015
8215
|
}
|
|
8016
8216
|
// --- CI/CD Variables ---
|
|
@@ -8018,24 +8218,24 @@ async function handleToolCall(params) {
|
|
|
8018
8218
|
const args = ListProjectVariablesSchema.parse(params.arguments);
|
|
8019
8219
|
const { project_id, ...options } = args;
|
|
8020
8220
|
const variables = await listProjectVariables(project_id, options);
|
|
8021
|
-
return { content: [{ type: "text", text: JSON.stringify(variables
|
|
8221
|
+
return { content: [{ type: "text", text: JSON.stringify(variables) }] };
|
|
8022
8222
|
}
|
|
8023
8223
|
case "get_project_variable": {
|
|
8024
8224
|
const args = GetProjectVariableSchema.parse(params.arguments);
|
|
8025
8225
|
const variable = await getProjectVariable(args.project_id, args.key, args.filter);
|
|
8026
|
-
return { content: [{ type: "text", text: JSON.stringify(variable
|
|
8226
|
+
return { content: [{ type: "text", text: JSON.stringify(variable) }] };
|
|
8027
8227
|
}
|
|
8028
8228
|
case "create_project_variable": {
|
|
8029
8229
|
const args = CreateProjectVariableSchema.parse(params.arguments);
|
|
8030
8230
|
const { project_id, ...options } = args;
|
|
8031
8231
|
const variable = await createProjectVariable(project_id, options);
|
|
8032
|
-
return { content: [{ type: "text", text: JSON.stringify(variable
|
|
8232
|
+
return { content: [{ type: "text", text: JSON.stringify(variable) }] };
|
|
8033
8233
|
}
|
|
8034
8234
|
case "update_project_variable": {
|
|
8035
8235
|
const args = UpdateProjectVariableSchema.parse(params.arguments);
|
|
8036
8236
|
const { project_id, key, ...options } = args;
|
|
8037
8237
|
const variable = await updateProjectVariable(project_id, key, options);
|
|
8038
|
-
return { content: [{ type: "text", text: JSON.stringify(variable
|
|
8238
|
+
return { content: [{ type: "text", text: JSON.stringify(variable) }] };
|
|
8039
8239
|
}
|
|
8040
8240
|
case "delete_project_variable": {
|
|
8041
8241
|
const args = DeleteProjectVariableSchema.parse(params.arguments);
|
|
@@ -8054,27 +8254,27 @@ async function handleToolCall(params) {
|
|
|
8054
8254
|
const args = ListGroupVariablesSchema.parse(params.arguments);
|
|
8055
8255
|
const { group_id, ...options } = args;
|
|
8056
8256
|
const variables = await listGroupVariables(group_id, options);
|
|
8057
|
-
return { content: [{ type: "text", text: JSON.stringify(variables
|
|
8257
|
+
return { content: [{ type: "text", text: JSON.stringify(variables) }] };
|
|
8058
8258
|
}
|
|
8059
8259
|
case "get_group_variable": {
|
|
8060
8260
|
rejectIfProjectScopedDeployment("get_group_variable");
|
|
8061
8261
|
const args = GetGroupVariableSchema.parse(params.arguments);
|
|
8062
8262
|
const variable = await getGroupVariable(args.group_id, args.key, args.filter);
|
|
8063
|
-
return { content: [{ type: "text", text: JSON.stringify(variable
|
|
8263
|
+
return { content: [{ type: "text", text: JSON.stringify(variable) }] };
|
|
8064
8264
|
}
|
|
8065
8265
|
case "create_group_variable": {
|
|
8066
8266
|
rejectIfProjectScopedDeployment("create_group_variable");
|
|
8067
8267
|
const args = CreateGroupVariableSchema.parse(params.arguments);
|
|
8068
8268
|
const { group_id, ...options } = args;
|
|
8069
8269
|
const variable = await createGroupVariable(group_id, options);
|
|
8070
|
-
return { content: [{ type: "text", text: JSON.stringify(variable
|
|
8270
|
+
return { content: [{ type: "text", text: JSON.stringify(variable) }] };
|
|
8071
8271
|
}
|
|
8072
8272
|
case "update_group_variable": {
|
|
8073
8273
|
rejectIfProjectScopedDeployment("update_group_variable");
|
|
8074
8274
|
const args = UpdateGroupVariableSchema.parse(params.arguments);
|
|
8075
8275
|
const { group_id, key, ...options } = args;
|
|
8076
8276
|
const variable = await updateGroupVariable(group_id, key, options);
|
|
8077
|
-
return { content: [{ type: "text", text: JSON.stringify(variable
|
|
8277
|
+
return { content: [{ type: "text", text: JSON.stringify(variable) }] };
|
|
8078
8278
|
}
|
|
8079
8279
|
case "delete_group_variable": {
|
|
8080
8280
|
rejectIfProjectScopedDeployment("delete_group_variable");
|
|
@@ -8094,7 +8294,7 @@ async function handleToolCall(params) {
|
|
|
8094
8294
|
const args = GetDependencyProxySettingsSchema.parse(params.arguments);
|
|
8095
8295
|
const settings = await getDependencyProxySettings(args.group_id);
|
|
8096
8296
|
return {
|
|
8097
|
-
content: [{ type: "text", text: JSON.stringify(settings
|
|
8297
|
+
content: [{ type: "text", text: JSON.stringify(settings) }],
|
|
8098
8298
|
};
|
|
8099
8299
|
}
|
|
8100
8300
|
case "update_dependency_proxy_settings": {
|
|
@@ -8103,7 +8303,7 @@ async function handleToolCall(params) {
|
|
|
8103
8303
|
const { group_id, ...options } = args;
|
|
8104
8304
|
const settings = await updateDependencyProxySettings(group_id, options);
|
|
8105
8305
|
return {
|
|
8106
|
-
content: [{ type: "text", text: JSON.stringify(settings
|
|
8306
|
+
content: [{ type: "text", text: JSON.stringify(settings) }],
|
|
8107
8307
|
};
|
|
8108
8308
|
}
|
|
8109
8309
|
case "list_dependency_proxy_blobs": {
|
|
@@ -8112,7 +8312,7 @@ async function handleToolCall(params) {
|
|
|
8112
8312
|
const { group_id, ...options } = args;
|
|
8113
8313
|
const result = await listDependencyProxyBlobs(group_id, options);
|
|
8114
8314
|
return {
|
|
8115
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
8315
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
8116
8316
|
};
|
|
8117
8317
|
}
|
|
8118
8318
|
case "purge_dependency_proxy_cache": {
|
|
@@ -8133,13 +8333,13 @@ async function handleToolCall(params) {
|
|
|
8133
8333
|
const args = MarkdownUploadRemoteSchema.parse(params.arguments);
|
|
8134
8334
|
const upload = await markdownUpload(args.project_id, undefined, args.content, args.filename);
|
|
8135
8335
|
return {
|
|
8136
|
-
content: [{ type: "text", text: JSON.stringify(upload
|
|
8336
|
+
content: [{ type: "text", text: JSON.stringify(upload) }],
|
|
8137
8337
|
};
|
|
8138
8338
|
}
|
|
8139
8339
|
const args = MarkdownUploadSchema.parse(params.arguments);
|
|
8140
8340
|
const upload = await markdownUpload(args.project_id, args.file_path);
|
|
8141
8341
|
return {
|
|
8142
|
-
content: [{ type: "text", text: JSON.stringify(upload
|
|
8342
|
+
content: [{ type: "text", text: JSON.stringify(upload) }],
|
|
8143
8343
|
};
|
|
8144
8344
|
}
|
|
8145
8345
|
case "download_attachment": {
|
|
@@ -8151,10 +8351,12 @@ async function handleToolCall(params) {
|
|
|
8151
8351
|
const mimeType = getImageMimeType(args.filename);
|
|
8152
8352
|
if (IS_REMOTE && !mimeType) {
|
|
8153
8353
|
const downloadUrl = buildDownloadUrl("attachment", {
|
|
8154
|
-
project_id: args.project_id,
|
|
8354
|
+
project_id: args.project_id,
|
|
8355
|
+
secret: args.secret,
|
|
8356
|
+
filename: args.filename,
|
|
8155
8357
|
});
|
|
8156
8358
|
return {
|
|
8157
|
-
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 }) }],
|
|
8158
8360
|
};
|
|
8159
8361
|
}
|
|
8160
8362
|
const result = await downloadAttachment(args.project_id, args.secret, args.filename, args.local_path);
|
|
@@ -8166,7 +8368,7 @@ async function handleToolCall(params) {
|
|
|
8166
8368
|
{ type: "image", data: base64, mimeType: result.mimeType },
|
|
8167
8369
|
{
|
|
8168
8370
|
type: "text",
|
|
8169
|
-
text: JSON.stringify({ filename: result.filename, mimeType: result.mimeType }
|
|
8371
|
+
text: JSON.stringify({ filename: result.filename, mimeType: result.mimeType }),
|
|
8170
8372
|
},
|
|
8171
8373
|
],
|
|
8172
8374
|
};
|
|
@@ -8175,7 +8377,7 @@ async function handleToolCall(params) {
|
|
|
8175
8377
|
content: [
|
|
8176
8378
|
{
|
|
8177
8379
|
type: "text",
|
|
8178
|
-
text: JSON.stringify({ success: true, file_path: result.savedPath }
|
|
8380
|
+
text: JSON.stringify({ success: true, file_path: result.savedPath }),
|
|
8179
8381
|
},
|
|
8180
8382
|
],
|
|
8181
8383
|
};
|
|
@@ -8184,7 +8386,7 @@ async function handleToolCall(params) {
|
|
|
8184
8386
|
const args = ListEventsSchema.parse(params.arguments);
|
|
8185
8387
|
const events = await listEvents(args);
|
|
8186
8388
|
return {
|
|
8187
|
-
content: [{ type: "text", text: JSON.stringify(events
|
|
8389
|
+
content: [{ type: "text", text: JSON.stringify(events) }],
|
|
8188
8390
|
};
|
|
8189
8391
|
}
|
|
8190
8392
|
case "get_project_events": {
|
|
@@ -8192,7 +8394,7 @@ async function handleToolCall(params) {
|
|
|
8192
8394
|
const { project_id, ...options } = args;
|
|
8193
8395
|
const events = await getProjectEvents(project_id, options);
|
|
8194
8396
|
return {
|
|
8195
|
-
content: [{ type: "text", text: JSON.stringify(events
|
|
8397
|
+
content: [{ type: "text", text: JSON.stringify(events) }],
|
|
8196
8398
|
};
|
|
8197
8399
|
}
|
|
8198
8400
|
case "list_releases": {
|
|
@@ -8200,14 +8402,14 @@ async function handleToolCall(params) {
|
|
|
8200
8402
|
const { project_id, ...options } = args;
|
|
8201
8403
|
const releases = await listReleases(project_id, options);
|
|
8202
8404
|
return {
|
|
8203
|
-
content: [{ type: "text", text: JSON.stringify(releases
|
|
8405
|
+
content: [{ type: "text", text: JSON.stringify(releases) }],
|
|
8204
8406
|
};
|
|
8205
8407
|
}
|
|
8206
8408
|
case "get_release": {
|
|
8207
8409
|
const args = GetReleaseSchema.parse(params.arguments);
|
|
8208
8410
|
const release = await getRelease(args.project_id, args.tag_name, args.include_html_description);
|
|
8209
8411
|
return {
|
|
8210
|
-
content: [{ type: "text", text: JSON.stringify(release
|
|
8412
|
+
content: [{ type: "text", text: JSON.stringify(release) }],
|
|
8211
8413
|
};
|
|
8212
8414
|
}
|
|
8213
8415
|
case "create_release": {
|
|
@@ -8215,7 +8417,7 @@ async function handleToolCall(params) {
|
|
|
8215
8417
|
const { project_id, ...options } = args;
|
|
8216
8418
|
const release = await createRelease(project_id, options);
|
|
8217
8419
|
return {
|
|
8218
|
-
content: [{ type: "text", text: JSON.stringify(release
|
|
8420
|
+
content: [{ type: "text", text: JSON.stringify(release) }],
|
|
8219
8421
|
};
|
|
8220
8422
|
}
|
|
8221
8423
|
case "update_release": {
|
|
@@ -8223,7 +8425,7 @@ async function handleToolCall(params) {
|
|
|
8223
8425
|
const { project_id, tag_name, ...options } = args;
|
|
8224
8426
|
const release = await updateRelease(project_id, tag_name, options);
|
|
8225
8427
|
return {
|
|
8226
|
-
content: [{ type: "text", text: JSON.stringify(release
|
|
8428
|
+
content: [{ type: "text", text: JSON.stringify(release) }],
|
|
8227
8429
|
};
|
|
8228
8430
|
}
|
|
8229
8431
|
case "delete_release": {
|
|
@@ -8254,10 +8456,12 @@ async function handleToolCall(params) {
|
|
|
8254
8456
|
const args = DownloadReleaseAssetSchema.parse(params.arguments);
|
|
8255
8457
|
if (IS_REMOTE) {
|
|
8256
8458
|
const downloadUrl = buildDownloadUrl("release-asset", {
|
|
8257
|
-
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,
|
|
8258
8462
|
});
|
|
8259
8463
|
return {
|
|
8260
|
-
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 }) }],
|
|
8261
8465
|
};
|
|
8262
8466
|
}
|
|
8263
8467
|
const assetContent = await downloadReleaseAsset(args.project_id, args.tag_name, args.direct_asset_path);
|
|
@@ -8270,14 +8474,14 @@ async function handleToolCall(params) {
|
|
|
8270
8474
|
const { project_id, ...options } = args;
|
|
8271
8475
|
const tags = await listTags(project_id, options);
|
|
8272
8476
|
return {
|
|
8273
|
-
content: [{ type: "text", text: JSON.stringify(tags
|
|
8477
|
+
content: [{ type: "text", text: JSON.stringify(tags) }],
|
|
8274
8478
|
};
|
|
8275
8479
|
}
|
|
8276
8480
|
case "get_tag": {
|
|
8277
8481
|
const args = GetTagSchema.parse(params.arguments);
|
|
8278
8482
|
const tag = await getTag(args.project_id, args.tag_name);
|
|
8279
8483
|
return {
|
|
8280
|
-
content: [{ type: "text", text: JSON.stringify(tag
|
|
8484
|
+
content: [{ type: "text", text: JSON.stringify(tag) }],
|
|
8281
8485
|
};
|
|
8282
8486
|
}
|
|
8283
8487
|
case "create_tag": {
|
|
@@ -8285,7 +8489,7 @@ async function handleToolCall(params) {
|
|
|
8285
8489
|
const { project_id, ...options } = args;
|
|
8286
8490
|
const tag = await createTag(project_id, options);
|
|
8287
8491
|
return {
|
|
8288
|
-
content: [{ type: "text", text: JSON.stringify(tag
|
|
8492
|
+
content: [{ type: "text", text: JSON.stringify(tag) }],
|
|
8289
8493
|
};
|
|
8290
8494
|
}
|
|
8291
8495
|
case "delete_tag": {
|
|
@@ -8304,30 +8508,28 @@ async function handleToolCall(params) {
|
|
|
8304
8508
|
const args = GetTagSignatureSchema.parse(params.arguments);
|
|
8305
8509
|
const signature = await getTagSignature(args.project_id, args.tag_name);
|
|
8306
8510
|
return {
|
|
8307
|
-
content: [{ type: "text", text: JSON.stringify(signature
|
|
8511
|
+
content: [{ type: "text", text: JSON.stringify(signature) }],
|
|
8308
8512
|
};
|
|
8309
8513
|
}
|
|
8310
8514
|
case "list_webhooks": {
|
|
8311
8515
|
const args = ListWebhooksSchema.parse(params.arguments);
|
|
8312
8516
|
const webhooks = await listWebhooks(args);
|
|
8313
8517
|
return {
|
|
8314
|
-
content: [{ type: "text", text: JSON.stringify(webhooks
|
|
8518
|
+
content: [{ type: "text", text: JSON.stringify(webhooks) }],
|
|
8315
8519
|
};
|
|
8316
8520
|
}
|
|
8317
8521
|
case "list_webhook_events": {
|
|
8318
8522
|
const args = ListWebhookEventsSchema.parse(params.arguments);
|
|
8319
8523
|
const events = await listWebhookEvents(args);
|
|
8320
8524
|
return {
|
|
8321
|
-
content: [{ type: "text", text: JSON.stringify(events
|
|
8525
|
+
content: [{ type: "text", text: JSON.stringify(events) }],
|
|
8322
8526
|
};
|
|
8323
8527
|
}
|
|
8324
8528
|
case "get_webhook_event": {
|
|
8325
8529
|
const args = GetWebhookEventSchema.parse(params.arguments);
|
|
8326
8530
|
const event = await getWebhookEvent(args);
|
|
8327
8531
|
if (!event) {
|
|
8328
|
-
const searchScope = args.page
|
|
8329
|
-
? `on page ${args.page}`
|
|
8330
|
-
: "in the 500 most recent events";
|
|
8532
|
+
const searchScope = args.page ? `on page ${args.page}` : "in the 500 most recent events";
|
|
8331
8533
|
return {
|
|
8332
8534
|
content: [
|
|
8333
8535
|
{
|
|
@@ -8338,7 +8540,7 @@ async function handleToolCall(params) {
|
|
|
8338
8540
|
};
|
|
8339
8541
|
}
|
|
8340
8542
|
return {
|
|
8341
|
-
content: [{ type: "text", text: JSON.stringify(event
|
|
8543
|
+
content: [{ type: "text", text: JSON.stringify(event) }],
|
|
8342
8544
|
};
|
|
8343
8545
|
}
|
|
8344
8546
|
case "health_check": {
|
|
@@ -8346,13 +8548,24 @@ async function handleToolCall(params) {
|
|
|
8346
8548
|
const url = new URL(`${getEffectiveApiUrl()}/user`);
|
|
8347
8549
|
const response = await fetch(url.toString(), getFetchConfig());
|
|
8348
8550
|
let authenticated = response.ok;
|
|
8349
|
-
if (!authenticated &&
|
|
8551
|
+
if (!authenticated &&
|
|
8552
|
+
(response.status === 401 || response.status === 403) &&
|
|
8553
|
+
(GITLAB_JOB_TOKEN || usesJobTokenHeader())) {
|
|
8350
8554
|
const jobUrl = new URL(`${getEffectiveApiUrl()}/job`);
|
|
8351
8555
|
const jobResponse = await fetch(jobUrl.toString(), getFetchConfig());
|
|
8352
8556
|
authenticated = jobResponse.ok;
|
|
8353
8557
|
}
|
|
8354
8558
|
return {
|
|
8355
|
-
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
|
+
],
|
|
8356
8569
|
};
|
|
8357
8570
|
}
|
|
8358
8571
|
case "get_branch": {
|
|
@@ -8366,7 +8579,7 @@ async function handleToolCall(params) {
|
|
|
8366
8579
|
const data = await response.json();
|
|
8367
8580
|
const branch = GitLabBranchSchema.parse(data);
|
|
8368
8581
|
return {
|
|
8369
|
-
content: [{ type: "text", text: JSON.stringify(branch
|
|
8582
|
+
content: [{ type: "text", text: JSON.stringify(branch) }],
|
|
8370
8583
|
};
|
|
8371
8584
|
}
|
|
8372
8585
|
case "list_branches": {
|
|
@@ -8389,7 +8602,7 @@ async function handleToolCall(params) {
|
|
|
8389
8602
|
const data = await response.json();
|
|
8390
8603
|
const branches = z.array(GitLabBranchSchema).parse(data);
|
|
8391
8604
|
return {
|
|
8392
|
-
content: [{ type: "text", text: JSON.stringify(branches
|
|
8605
|
+
content: [{ type: "text", text: JSON.stringify(branches) }],
|
|
8393
8606
|
};
|
|
8394
8607
|
}
|
|
8395
8608
|
case "delete_branch": {
|
|
@@ -8402,7 +8615,7 @@ async function handleToolCall(params) {
|
|
|
8402
8615
|
});
|
|
8403
8616
|
await handleGitLabError(response);
|
|
8404
8617
|
return {
|
|
8405
|
-
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 }) }],
|
|
8406
8619
|
};
|
|
8407
8620
|
}
|
|
8408
8621
|
case "list_protected_branches": {
|
|
@@ -8422,7 +8635,7 @@ async function handleToolCall(params) {
|
|
|
8422
8635
|
await handleGitLabError(response);
|
|
8423
8636
|
const data = z.array(GitLabProtectedBranchSchema).parse(await response.json());
|
|
8424
8637
|
return {
|
|
8425
|
-
content: [{ type: "text", text: JSON.stringify(data
|
|
8638
|
+
content: [{ type: "text", text: JSON.stringify(data) }],
|
|
8426
8639
|
};
|
|
8427
8640
|
}
|
|
8428
8641
|
case "get_protected_branch": {
|
|
@@ -8436,7 +8649,7 @@ async function handleToolCall(params) {
|
|
|
8436
8649
|
await handleGitLabError(response);
|
|
8437
8650
|
const data = GitLabProtectedBranchSchema.parse(await response.json());
|
|
8438
8651
|
return {
|
|
8439
|
-
content: [{ type: "text", text: JSON.stringify(data
|
|
8652
|
+
content: [{ type: "text", text: JSON.stringify(data) }],
|
|
8440
8653
|
};
|
|
8441
8654
|
}
|
|
8442
8655
|
case "protect_branch": {
|
|
@@ -8463,7 +8676,7 @@ async function handleToolCall(params) {
|
|
|
8463
8676
|
await handleGitLabError(response);
|
|
8464
8677
|
const data = GitLabProtectedBranchSchema.parse(await response.json());
|
|
8465
8678
|
return {
|
|
8466
|
-
content: [{ type: "text", text: JSON.stringify(data
|
|
8679
|
+
content: [{ type: "text", text: JSON.stringify(data) }],
|
|
8467
8680
|
};
|
|
8468
8681
|
}
|
|
8469
8682
|
case "unprotect_branch": {
|
|
@@ -8477,7 +8690,7 @@ async function handleToolCall(params) {
|
|
|
8477
8690
|
});
|
|
8478
8691
|
await handleGitLabError(response);
|
|
8479
8692
|
return {
|
|
8480
|
-
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 }) }],
|
|
8481
8694
|
};
|
|
8482
8695
|
}
|
|
8483
8696
|
case "update_default_branch": {
|
|
@@ -8493,7 +8706,7 @@ async function handleToolCall(params) {
|
|
|
8493
8706
|
await handleGitLabError(response);
|
|
8494
8707
|
const data = await response.json();
|
|
8495
8708
|
return {
|
|
8496
|
-
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 }) }],
|
|
8497
8710
|
};
|
|
8498
8711
|
}
|
|
8499
8712
|
default:
|
|
@@ -8672,7 +8885,9 @@ function registerDownloadProxy(app, maxRequestsPerMinute = Number.parseInt(proce
|
|
|
8672
8885
|
case "release-asset": {
|
|
8673
8886
|
const { project_id, tag_name, direct_asset_path } = req.query;
|
|
8674
8887
|
if (!project_id || !tag_name || !direct_asset_path) {
|
|
8675
|
-
res
|
|
8888
|
+
res
|
|
8889
|
+
.status(400)
|
|
8890
|
+
.json({ error: "project_id, tag_name, and direct_asset_path are required" });
|
|
8676
8891
|
return;
|
|
8677
8892
|
}
|
|
8678
8893
|
const effectiveProjectId = getEffectiveProjectId(decodeURIComponent(project_id));
|
|
@@ -9079,7 +9294,7 @@ async function startStreamableHTTPServer() {
|
|
|
9079
9294
|
token: effective.token,
|
|
9080
9295
|
lastUsed: Date.now(),
|
|
9081
9296
|
apiUrl: effective.apiUrl,
|
|
9082
|
-
publicBaseUrl: getForwardedPublicBaseUrl(req),
|
|
9297
|
+
publicBaseUrl: getForwardedPublicBaseUrl(req, MCP_TRUST_PROXY),
|
|
9083
9298
|
};
|
|
9084
9299
|
// Step 4: create a fresh transport per request.
|
|
9085
9300
|
const transport = isInit
|
|
@@ -9273,16 +9488,14 @@ async function startStreamableHTTPServer() {
|
|
|
9273
9488
|
// Streamable HTTP endpoint - handles both session creation and message handling
|
|
9274
9489
|
app.post("/mcp", mcpBearerAuth, async (req, res) => {
|
|
9275
9490
|
const sessionId = readMcpSessionIdHeader(req);
|
|
9276
|
-
const publicBaseUrl = getForwardedPublicBaseUrl(req);
|
|
9491
|
+
const publicBaseUrl = getForwardedPublicBaseUrl(req, MCP_TRUST_PROXY);
|
|
9277
9492
|
// Track request
|
|
9278
9493
|
metrics.requestsProcessed++;
|
|
9279
9494
|
// Stateless-mode branch: bypass authBySession / streamableTransports
|
|
9280
9495
|
// entirely and derive the session auth from either the current request
|
|
9281
9496
|
// headers (init) or a sealed Mcp-Session-Id (subsequent requests).
|
|
9282
9497
|
// Rate limiting is disabled here because there is no shared counter.
|
|
9283
|
-
if (OAUTH_STATELESS_MODE &&
|
|
9284
|
-
STATELESS_MATERIAL &&
|
|
9285
|
-
(REMOTE_AUTHORIZATION || GITLAB_MCP_OAUTH)) {
|
|
9498
|
+
if (OAUTH_STATELESS_MODE && STATELESS_MATERIAL && (REMOTE_AUTHORIZATION || GITLAB_MCP_OAUTH)) {
|
|
9286
9499
|
await handleStatelessMcpRequest(req, res, STATELESS_MATERIAL, OAUTH_STATELESS_SESSION_TTL_SECONDS);
|
|
9287
9500
|
return;
|
|
9288
9501
|
}
|
|
@@ -9307,9 +9520,11 @@ async function startStreamableHTTPServer() {
|
|
|
9307
9520
|
// Handle remote authorization: extract and store auth headers per session
|
|
9308
9521
|
if (REMOTE_AUTHORIZATION) {
|
|
9309
9522
|
const authData = parseAuthHeaders(req);
|
|
9523
|
+
const allowUnauthenticatedDiscovery = GITLAB_ALLOW_UNAUTHENTICATED_TOOL_DISCOVERY &&
|
|
9524
|
+
isUnauthenticatedDiscoveryRequestBody(req.body);
|
|
9310
9525
|
if (sessionId && !authBySession[sessionId]) {
|
|
9311
|
-
// New session: require auth headers
|
|
9312
|
-
if (!authData) {
|
|
9526
|
+
// New session: require auth headers unless public discovery was explicitly enabled.
|
|
9527
|
+
if (!authData && !allowUnauthenticatedDiscovery) {
|
|
9313
9528
|
metrics.authFailures++;
|
|
9314
9529
|
res.status(401).json({
|
|
9315
9530
|
error: "Missing Private-Token, JOB-TOKEN, or Authorization header",
|
|
@@ -9317,10 +9532,16 @@ async function startStreamableHTTPServer() {
|
|
|
9317
9532
|
});
|
|
9318
9533
|
return;
|
|
9319
9534
|
}
|
|
9320
|
-
// Store auth
|
|
9321
|
-
|
|
9322
|
-
|
|
9323
|
-
|
|
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
|
+
}
|
|
9324
9545
|
}
|
|
9325
9546
|
else if (sessionId && authData) {
|
|
9326
9547
|
// Existing session: allow auth rotation/update
|
|
@@ -9493,25 +9714,112 @@ async function startStreamableHTTPServer() {
|
|
|
9493
9714
|
message: "GET /mcp is not supported when STREAMABLE_HTTP is enabled. Use POST to communicate with the MCP server.",
|
|
9494
9715
|
});
|
|
9495
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
|
+
};
|
|
9496
9817
|
// Metrics endpoint
|
|
9497
9818
|
app.get("/metrics", (_req, res) => {
|
|
9498
|
-
res.
|
|
9499
|
-
|
|
9500
|
-
|
|
9501
|
-
|
|
9502
|
-
gitlabClientPool: clientPool.getStats(),
|
|
9503
|
-
uptime: process.uptime(),
|
|
9504
|
-
memoryUsage: process.memoryUsage(),
|
|
9505
|
-
config: {
|
|
9506
|
-
maxSessions: MAX_SESSIONS,
|
|
9507
|
-
maxRequestsPerMinute: MAX_REQUESTS_PER_MINUTE,
|
|
9508
|
-
sessionTimeoutSeconds: SESSION_TIMEOUT_SECONDS,
|
|
9509
|
-
remoteAuthEnabled: REMOTE_AUTHORIZATION,
|
|
9510
|
-
mcpOAuthEnabled: GITLAB_MCP_OAUTH,
|
|
9511
|
-
statelessModeEnabled: OAUTH_STATELESS_MODE && STATELESS_MATERIAL !== null,
|
|
9512
|
-
statelessRotationKey: OAUTH_STATELESS_MODE && STATELESS_MATERIAL?.previous != null,
|
|
9513
|
-
},
|
|
9514
|
-
});
|
|
9819
|
+
res.type("text/plain; version=0.0.4").send(formatPrometheusMetrics());
|
|
9820
|
+
});
|
|
9821
|
+
app.get("/metrics.json", (_req, res) => {
|
|
9822
|
+
res.json(getMetricsSnapshot());
|
|
9515
9823
|
});
|
|
9516
9824
|
// Health check endpoint
|
|
9517
9825
|
app.get("/health", (_req, res) => {
|