@zereight/mcp-gitlab 1.0.50 → 1.0.52
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 +26 -12
- package/build/index.js +174 -49
- package/build/schemas.js +176 -248
- package/build/scripts/generate-tools-readme.js +12 -12
- package/build/tests/integration.test.js +151 -0
- package/build/tests/unit.test.js +122 -0
- package/package.json +14 -3
package/README.md
CHANGED
|
@@ -26,7 +26,9 @@ When using with the Claude App, you need to set up your API key and URLs directl
|
|
|
26
26
|
"GITLAB_PERSONAL_ACCESS_TOKEN": "your_gitlab_token",
|
|
27
27
|
"GITLAB_API_URL": "your_gitlab_api_url",
|
|
28
28
|
"GITLAB_READ_ONLY_MODE": "false",
|
|
29
|
-
"USE_GITLAB_WIKI": "
|
|
29
|
+
"USE_GITLAB_WIKI": "false", // use wiki api?
|
|
30
|
+
"USE_MILESTONE": "false", // use milestone api?
|
|
31
|
+
"USE_PIPELINE": "false" // use pipeline api?
|
|
30
32
|
}
|
|
31
33
|
}
|
|
32
34
|
}
|
|
@@ -52,13 +54,19 @@ When using with the Claude App, you need to set up your API key and URLs directl
|
|
|
52
54
|
"GITLAB_READ_ONLY_MODE",
|
|
53
55
|
"-e",
|
|
54
56
|
"USE_GITLAB_WIKI",
|
|
57
|
+
"-e",
|
|
58
|
+
"USE_MILESTONE",
|
|
59
|
+
"-e",
|
|
60
|
+
"USE_PIPELINE",
|
|
55
61
|
"iwakitakuma/gitlab-mcp"
|
|
56
62
|
],
|
|
57
63
|
"env": {
|
|
58
64
|
"GITLAB_PERSONAL_ACCESS_TOKEN": "your_gitlab_token",
|
|
59
65
|
"GITLAB_API_URL": "https://gitlab.com/api/v4", // Optional, for self-hosted GitLab
|
|
60
66
|
"GITLAB_READ_ONLY_MODE": "false",
|
|
61
|
-
"USE_GITLAB_WIKI": "true"
|
|
67
|
+
"USE_GITLAB_WIKI": "true",
|
|
68
|
+
"USE_MILESTONE": "true",
|
|
69
|
+
"USE_PIPELINE": "true"
|
|
62
70
|
}
|
|
63
71
|
}
|
|
64
72
|
}
|
|
@@ -77,10 +85,13 @@ $ sh scripts/image_push.sh docker_user_name
|
|
|
77
85
|
- `GITLAB_API_URL`: Your GitLab API URL. (Default: `https://gitlab.com/api/v4`)
|
|
78
86
|
- `GITLAB_READ_ONLY_MODE`: When set to 'true', restricts the server to only expose read-only operations. Useful for enhanced security or when write access is not needed. Also useful for using with Cursor and it's 40 tool limit.
|
|
79
87
|
- `USE_GITLAB_WIKI`: When set to 'true', enables the wiki-related tools (list_wiki_pages, get_wiki_page, create_wiki_page, update_wiki_page, delete_wiki_page). By default, wiki features are disabled.
|
|
88
|
+
- `USE_MILESTONE`: When set to 'true', enables the milestone-related tools (list_milestones, get_milestone, create_milestone, edit_milestone, delete_milestone, get_milestone_issue, get_milestone_merge_requests, promote_milestone, get_milestone_burndown_events). By default, milestone features are disabled.
|
|
89
|
+
- `USE_PIPELINE`: When set to 'true', enables the pipeline-related tools (list_pipelines, get_pipeline, list_pipeline_jobs, get_pipeline_job, get_pipeline_job_output, create_pipeline, retry_pipeline, cancel_pipeline). By default, pipeline features are disabled.
|
|
80
90
|
|
|
81
91
|
## Tools 🛠️
|
|
82
92
|
|
|
83
93
|
+<!-- TOOLS-START -->
|
|
94
|
+
|
|
84
95
|
1. `create_or_update_file` - Create or update a single file in a GitLab project
|
|
85
96
|
2. `search_repositories` - Search for GitLab projects
|
|
86
97
|
3. `create_repository` - Create a new GitLab project
|
|
@@ -131,14 +142,17 @@ $ sh scripts/image_push.sh docker_user_name
|
|
|
131
142
|
48. `list_pipeline_jobs` - List all jobs in a specific pipeline
|
|
132
143
|
49. `get_pipeline_job` - Get details of a GitLab pipeline job number
|
|
133
144
|
50. `get_pipeline_job_output` - Get the output/trace of a GitLab pipeline job number
|
|
134
|
-
51. `
|
|
135
|
-
52. `
|
|
136
|
-
53. `
|
|
137
|
-
54. `
|
|
138
|
-
55. `
|
|
139
|
-
56. `
|
|
140
|
-
57. `
|
|
141
|
-
58. `
|
|
142
|
-
59. `
|
|
143
|
-
60. `
|
|
145
|
+
51. `create_pipeline` - Create a new pipeline for a branch or tag
|
|
146
|
+
52. `retry_pipeline` - Retry a failed or canceled pipeline
|
|
147
|
+
53. `cancel_pipeline` - Cancel a running pipeline
|
|
148
|
+
54. `list_merge_requests` - List merge requests in a GitLab project with filtering options
|
|
149
|
+
55. `list_milestones` - List milestones in a GitLab project with filtering options
|
|
150
|
+
56. `get_milestone` - Get details of a specific milestone
|
|
151
|
+
57. `create_milestone` - Create a new milestone in a GitLab project
|
|
152
|
+
58. `edit_milestone ` - Edit an existing milestone in a GitLab project
|
|
153
|
+
59. `delete_milestone` - Delete a milestone from a GitLab project
|
|
154
|
+
60. `get_milestone_issue` - Get issues associated with a specific milestone
|
|
155
|
+
61. `get_milestone_merge_requests` - Get merge requests associated with a specific milestone
|
|
156
|
+
62. `promote_milestone` - Promote a milestone to the next stage
|
|
157
|
+
63. `get_milestone_burndown_events` - Get burndown events for a specific milestone
|
|
144
158
|
<!-- TOOLS-END -->
|
package/build/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
-
import { CallToolRequestSchema, ListToolsRequestSchema
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
5
5
|
import fetch from "node-fetch";
|
|
6
6
|
import { SocksProxyAgent } from "socks-proxy-agent";
|
|
7
7
|
import { HttpsProxyAgent } from "https-proxy-agent";
|
|
@@ -13,7 +13,7 @@ import { dirname } from "path";
|
|
|
13
13
|
import fs from "fs";
|
|
14
14
|
import path from "path";
|
|
15
15
|
import { URL } from "url";
|
|
16
|
-
import { GitLabForkSchema, GitLabReferenceSchema, GitLabRepositorySchema, GitLabIssueSchema, GitLabMergeRequestSchema, GitLabContentSchema, GitLabCreateUpdateFileResponseSchema, GitLabSearchResponseSchema, GitLabTreeSchema, GitLabCommitSchema, GitLabNamespaceSchema, GitLabNamespaceExistsResponseSchema, GitLabProjectSchema, CreateOrUpdateFileSchema, SearchRepositoriesSchema, CreateRepositorySchema, GetFileContentsSchema, PushFilesSchema, CreateIssueSchema, CreateMergeRequestSchema, ForkRepositorySchema, CreateBranchSchema, GitLabMergeRequestDiffSchema, GetMergeRequestSchema, GetMergeRequestDiffsSchema, UpdateMergeRequestSchema, ListIssuesSchema, GetIssueSchema, UpdateIssueSchema, DeleteIssueSchema, GitLabIssueLinkSchema, GitLabIssueWithLinkDetailsSchema, ListIssueLinksSchema, ListIssueDiscussionsSchema, GetIssueLinkSchema, CreateIssueLinkSchema, DeleteIssueLinkSchema, ListNamespacesSchema, GetNamespaceSchema, VerifyNamespaceSchema, GetProjectSchema, ListProjectsSchema, ListLabelsSchema, GetLabelSchema, CreateLabelSchema, UpdateLabelSchema, DeleteLabelSchema, CreateNoteSchema, CreateMergeRequestThreadSchema, ListGroupProjectsSchema, ListWikiPagesSchema, GetWikiPageSchema, CreateWikiPageSchema, UpdateWikiPageSchema, DeleteWikiPageSchema, GitLabWikiPageSchema, GetRepositoryTreeSchema, GitLabTreeItemSchema, GitLabPipelineSchema, GetPipelineSchema, ListPipelinesSchema, ListPipelineJobsSchema,
|
|
16
|
+
import { GitLabForkSchema, GitLabReferenceSchema, GitLabRepositorySchema, GitLabIssueSchema, GitLabMergeRequestSchema, GitLabContentSchema, GitLabCreateUpdateFileResponseSchema, GitLabSearchResponseSchema, GitLabTreeSchema, GitLabCommitSchema, GitLabNamespaceSchema, GitLabNamespaceExistsResponseSchema, GitLabProjectSchema, CreateOrUpdateFileSchema, SearchRepositoriesSchema, CreateRepositorySchema, GetFileContentsSchema, PushFilesSchema, CreateIssueSchema, CreateMergeRequestSchema, ForkRepositorySchema, CreateBranchSchema, GitLabMergeRequestDiffSchema, GetMergeRequestSchema, GetMergeRequestDiffsSchema, UpdateMergeRequestSchema, ListIssuesSchema, GetIssueSchema, UpdateIssueSchema, DeleteIssueSchema, GitLabIssueLinkSchema, GitLabIssueWithLinkDetailsSchema, ListIssueLinksSchema, ListIssueDiscussionsSchema, GetIssueLinkSchema, CreateIssueLinkSchema, DeleteIssueLinkSchema, ListNamespacesSchema, GetNamespaceSchema, VerifyNamespaceSchema, GetProjectSchema, ListProjectsSchema, ListLabelsSchema, GetLabelSchema, CreateLabelSchema, UpdateLabelSchema, DeleteLabelSchema, CreateNoteSchema, CreateMergeRequestThreadSchema, ListGroupProjectsSchema, ListWikiPagesSchema, GetWikiPageSchema, CreateWikiPageSchema, UpdateWikiPageSchema, DeleteWikiPageSchema, GitLabWikiPageSchema, GetRepositoryTreeSchema, GitLabTreeItemSchema, GitLabPipelineSchema, GetPipelineSchema, ListPipelinesSchema, ListPipelineJobsSchema, CreatePipelineSchema, RetryPipelineSchema, CancelPipelineSchema,
|
|
17
17
|
// pipeline job schemas
|
|
18
18
|
GetPipelineJobOutputSchema, GitLabPipelineJobSchema,
|
|
19
19
|
// Discussion Schemas
|
|
@@ -48,6 +48,8 @@ const server = new Server({
|
|
|
48
48
|
const GITLAB_PERSONAL_ACCESS_TOKEN = process.env.GITLAB_PERSONAL_ACCESS_TOKEN;
|
|
49
49
|
const GITLAB_READ_ONLY_MODE = process.env.GITLAB_READ_ONLY_MODE === "true";
|
|
50
50
|
const USE_GITLAB_WIKI = process.env.USE_GITLAB_WIKI === "true";
|
|
51
|
+
const USE_MILESTONE = process.env.USE_MILESTONE === "true";
|
|
52
|
+
const USE_PIPELINE = process.env.USE_PIPELINE === "true";
|
|
51
53
|
// Add proxy configuration
|
|
52
54
|
const HTTP_PROXY = process.env.HTTP_PROXY;
|
|
53
55
|
const HTTPS_PROXY = process.env.HTTPS_PROXY;
|
|
@@ -338,6 +340,21 @@ const allTools = [
|
|
|
338
340
|
description: "Get the output/trace of a GitLab pipeline job number",
|
|
339
341
|
inputSchema: zodToJsonSchema(GetPipelineJobOutputSchema),
|
|
340
342
|
},
|
|
343
|
+
{
|
|
344
|
+
name: "create_pipeline",
|
|
345
|
+
description: "Create a new pipeline for a branch or tag",
|
|
346
|
+
inputSchema: zodToJsonSchema(CreatePipelineSchema),
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
name: "retry_pipeline",
|
|
350
|
+
description: "Retry a failed or canceled pipeline",
|
|
351
|
+
inputSchema: zodToJsonSchema(RetryPipelineSchema),
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
name: "cancel_pipeline",
|
|
355
|
+
description: "Cancel a running pipeline",
|
|
356
|
+
inputSchema: zodToJsonSchema(CancelPipelineSchema),
|
|
357
|
+
},
|
|
341
358
|
{
|
|
342
359
|
name: "list_merge_requests",
|
|
343
360
|
description: "List merge requests in a GitLab project with filtering options",
|
|
@@ -421,6 +438,8 @@ const readOnlyTools = [
|
|
|
421
438
|
"get_milestone_issue",
|
|
422
439
|
"get_milestone_merge_requests",
|
|
423
440
|
"get_milestone_burndown_events",
|
|
441
|
+
"list_wiki_pages",
|
|
442
|
+
"get_wiki_page",
|
|
424
443
|
];
|
|
425
444
|
// Define which tools are related to wiki and can be toggled by USE_GITLAB_WIKI
|
|
426
445
|
const wikiToolNames = [
|
|
@@ -431,6 +450,29 @@ const wikiToolNames = [
|
|
|
431
450
|
"delete_wiki_page",
|
|
432
451
|
"upload_wiki_attachment",
|
|
433
452
|
];
|
|
453
|
+
// Define which tools are related to milestones and can be toggled by USE_MILESTONE
|
|
454
|
+
const milestoneToolNames = [
|
|
455
|
+
"list_milestones",
|
|
456
|
+
"get_milestone",
|
|
457
|
+
"create_milestone",
|
|
458
|
+
"edit_milestone",
|
|
459
|
+
"delete_milestone",
|
|
460
|
+
"get_milestone_issue",
|
|
461
|
+
"get_milestone_merge_requests",
|
|
462
|
+
"promote_milestone",
|
|
463
|
+
"get_milestone_burndown_events",
|
|
464
|
+
];
|
|
465
|
+
// Define which tools are related to pipelines and can be toggled by USE_PIPELINE
|
|
466
|
+
const pipelineToolNames = [
|
|
467
|
+
"list_pipelines",
|
|
468
|
+
"get_pipeline",
|
|
469
|
+
"list_pipeline_jobs",
|
|
470
|
+
"get_pipeline_job",
|
|
471
|
+
"get_pipeline_job_output",
|
|
472
|
+
"create_pipeline",
|
|
473
|
+
"retry_pipeline",
|
|
474
|
+
"cancel_pipeline",
|
|
475
|
+
];
|
|
434
476
|
/**
|
|
435
477
|
* Smart URL handling for GitLab API
|
|
436
478
|
*
|
|
@@ -444,8 +486,7 @@ function normalizeGitLabApiUrl(url) {
|
|
|
444
486
|
// Remove trailing slash if present
|
|
445
487
|
let normalizedUrl = url.endsWith("/") ? url.slice(0, -1) : url;
|
|
446
488
|
// Check if URL already has /api/v4
|
|
447
|
-
if (!normalizedUrl.endsWith("/api/v4") &&
|
|
448
|
-
!normalizedUrl.endsWith("/api/v4/")) {
|
|
489
|
+
if (!normalizedUrl.endsWith("/api/v4") && !normalizedUrl.endsWith("/api/v4/")) {
|
|
449
490
|
// Append /api/v4 if not already present
|
|
450
491
|
normalizedUrl = `${normalizedUrl}/api/v4`;
|
|
451
492
|
}
|
|
@@ -468,8 +509,7 @@ async function handleGitLabError(response) {
|
|
|
468
509
|
if (!response.ok) {
|
|
469
510
|
const errorBody = await response.text();
|
|
470
511
|
// Check specifically for Rate Limit error
|
|
471
|
-
if (response.status === 403 &&
|
|
472
|
-
errorBody.includes("User API Key Rate limit exceeded")) {
|
|
512
|
+
if (response.status === 403 && errorBody.includes("User API Key Rate limit exceeded")) {
|
|
473
513
|
console.error("GitLab API Rate Limit Exceeded:", errorBody);
|
|
474
514
|
console.log("User API Key Rate limit exceeded. Please try again later.");
|
|
475
515
|
throw new Error(`GitLab API Rate Limit Exceeded: ${errorBody}`);
|
|
@@ -1095,7 +1135,7 @@ async function createTree(projectId, files, ref) {
|
|
|
1095
1135
|
...DEFAULT_FETCH_CONFIG,
|
|
1096
1136
|
method: "POST",
|
|
1097
1137
|
body: JSON.stringify({
|
|
1098
|
-
files: files.map(
|
|
1138
|
+
files: files.map(file => ({
|
|
1099
1139
|
file_path: file.path,
|
|
1100
1140
|
content: file.content,
|
|
1101
1141
|
encoding: "text",
|
|
@@ -1132,7 +1172,7 @@ async function createCommit(projectId, message, branch, actions) {
|
|
|
1132
1172
|
body: JSON.stringify({
|
|
1133
1173
|
branch,
|
|
1134
1174
|
commit_message: message,
|
|
1135
|
-
actions: actions.map(
|
|
1175
|
+
actions: actions.map(action => ({
|
|
1136
1176
|
action: "create",
|
|
1137
1177
|
file_path: action.path,
|
|
1138
1178
|
content: action.content,
|
|
@@ -1844,6 +1884,69 @@ async function getPipelineJobOutput(projectId, jobId) {
|
|
|
1844
1884
|
await handleGitLabError(response);
|
|
1845
1885
|
return await response.text();
|
|
1846
1886
|
}
|
|
1887
|
+
/**
|
|
1888
|
+
* Create a new pipeline
|
|
1889
|
+
*
|
|
1890
|
+
* @param {string} projectId - The ID or URL-encoded path of the project
|
|
1891
|
+
* @param {string} ref - The branch or tag to run the pipeline on
|
|
1892
|
+
* @param {Array} variables - Optional variables for the pipeline
|
|
1893
|
+
* @returns {Promise<GitLabPipeline>} The created pipeline
|
|
1894
|
+
*/
|
|
1895
|
+
async function createPipeline(projectId, ref, variables) {
|
|
1896
|
+
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1897
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/pipeline`);
|
|
1898
|
+
const body = { ref };
|
|
1899
|
+
if (variables && variables.length > 0) {
|
|
1900
|
+
body.variables = variables.reduce((acc, { key, value }) => {
|
|
1901
|
+
acc[key] = value;
|
|
1902
|
+
return acc;
|
|
1903
|
+
}, {});
|
|
1904
|
+
}
|
|
1905
|
+
const response = await fetch(url.toString(), {
|
|
1906
|
+
method: "POST",
|
|
1907
|
+
headers: DEFAULT_HEADERS,
|
|
1908
|
+
body: JSON.stringify(body),
|
|
1909
|
+
});
|
|
1910
|
+
await handleGitLabError(response);
|
|
1911
|
+
const data = await response.json();
|
|
1912
|
+
return GitLabPipelineSchema.parse(data);
|
|
1913
|
+
}
|
|
1914
|
+
/**
|
|
1915
|
+
* Retry a pipeline
|
|
1916
|
+
*
|
|
1917
|
+
* @param {string} projectId - The ID or URL-encoded path of the project
|
|
1918
|
+
* @param {number} pipelineId - The ID of the pipeline to retry
|
|
1919
|
+
* @returns {Promise<GitLabPipeline>} The retried pipeline
|
|
1920
|
+
*/
|
|
1921
|
+
async function retryPipeline(projectId, pipelineId) {
|
|
1922
|
+
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1923
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/pipelines/${pipelineId}/retry`);
|
|
1924
|
+
const response = await fetch(url.toString(), {
|
|
1925
|
+
method: "POST",
|
|
1926
|
+
headers: DEFAULT_HEADERS,
|
|
1927
|
+
});
|
|
1928
|
+
await handleGitLabError(response);
|
|
1929
|
+
const data = await response.json();
|
|
1930
|
+
return GitLabPipelineSchema.parse(data);
|
|
1931
|
+
}
|
|
1932
|
+
/**
|
|
1933
|
+
* Cancel a pipeline
|
|
1934
|
+
*
|
|
1935
|
+
* @param {string} projectId - The ID or URL-encoded path of the project
|
|
1936
|
+
* @param {number} pipelineId - The ID of the pipeline to cancel
|
|
1937
|
+
* @returns {Promise<GitLabPipeline>} The canceled pipeline
|
|
1938
|
+
*/
|
|
1939
|
+
async function cancelPipeline(projectId, pipelineId) {
|
|
1940
|
+
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
1941
|
+
const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/pipelines/${pipelineId}/cancel`);
|
|
1942
|
+
const response = await fetch(url.toString(), {
|
|
1943
|
+
method: "POST",
|
|
1944
|
+
headers: DEFAULT_HEADERS,
|
|
1945
|
+
});
|
|
1946
|
+
await handleGitLabError(response);
|
|
1947
|
+
const data = await response.json();
|
|
1948
|
+
return GitLabPipelineSchema.parse(data);
|
|
1949
|
+
}
|
|
1847
1950
|
/**
|
|
1848
1951
|
* Get the repository tree for a project
|
|
1849
1952
|
* @param {string} projectId - The ID or URL-encoded path of the project
|
|
@@ -1892,7 +1995,7 @@ async function listProjectMilestones(projectId, options) {
|
|
|
1892
1995
|
Object.entries(options).forEach(([key, value]) => {
|
|
1893
1996
|
if (value !== undefined) {
|
|
1894
1997
|
if (key === "iids" && Array.isArray(value) && value.length > 0) {
|
|
1895
|
-
value.forEach(
|
|
1998
|
+
value.forEach(iid => {
|
|
1896
1999
|
url.searchParams.append("iids[]", iid.toString());
|
|
1897
2000
|
});
|
|
1898
2001
|
}
|
|
@@ -2044,18 +2147,24 @@ async function getMilestoneBurndownEvents(projectId, milestoneId) {
|
|
|
2044
2147
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
2045
2148
|
// Apply read-only filter first
|
|
2046
2149
|
const tools0 = GITLAB_READ_ONLY_MODE
|
|
2047
|
-
? allTools.filter(
|
|
2150
|
+
? allTools.filter(tool => readOnlyTools.includes(tool.name))
|
|
2048
2151
|
: allTools;
|
|
2049
2152
|
// Toggle wiki tools by USE_GITLAB_WIKI flag
|
|
2050
|
-
|
|
2153
|
+
const tools1 = USE_GITLAB_WIKI
|
|
2051
2154
|
? tools0
|
|
2052
|
-
: tools0.filter(
|
|
2155
|
+
: tools0.filter(tool => !wikiToolNames.includes(tool.name));
|
|
2156
|
+
// Toggle milestone tools by USE_MILESTONE flag
|
|
2157
|
+
const tools2 = USE_MILESTONE
|
|
2158
|
+
? tools1
|
|
2159
|
+
: tools1.filter(tool => !milestoneToolNames.includes(tool.name));
|
|
2160
|
+
// Toggle pipeline tools by USE_PIPELINE flag
|
|
2161
|
+
let tools = USE_PIPELINE
|
|
2162
|
+
? tools2
|
|
2163
|
+
: tools2.filter(tool => !pipelineToolNames.includes(tool.name));
|
|
2053
2164
|
// <<< START: Gemini 호환성을 위해 $schema 제거 >>>
|
|
2054
|
-
tools = tools.map(
|
|
2165
|
+
tools = tools.map(tool => {
|
|
2055
2166
|
// inputSchema가 존재하고 객체인지 확인
|
|
2056
|
-
if (tool.inputSchema &&
|
|
2057
|
-
typeof tool.inputSchema === "object" &&
|
|
2058
|
-
tool.inputSchema !== null) {
|
|
2167
|
+
if (tool.inputSchema && typeof tool.inputSchema === "object" && tool.inputSchema !== null) {
|
|
2059
2168
|
// $schema 키가 존재하면 삭제
|
|
2060
2169
|
if ("$schema" in tool.inputSchema) {
|
|
2061
2170
|
// 불변성을 위해 새로운 객체 생성 (선택적이지만 권장)
|
|
@@ -2083,9 +2192,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2083
2192
|
try {
|
|
2084
2193
|
const forkedProject = await forkProject(forkArgs.project_id, forkArgs.namespace);
|
|
2085
2194
|
return {
|
|
2086
|
-
content: [
|
|
2087
|
-
{ type: "text", text: JSON.stringify(forkedProject, null, 2) },
|
|
2088
|
-
],
|
|
2195
|
+
content: [{ type: "text", text: JSON.stringify(forkedProject, null, 2) }],
|
|
2089
2196
|
};
|
|
2090
2197
|
}
|
|
2091
2198
|
catch (forkError) {
|
|
@@ -2129,9 +2236,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2129
2236
|
const args = CreateRepositorySchema.parse(request.params.arguments);
|
|
2130
2237
|
const repository = await createRepository(args);
|
|
2131
2238
|
return {
|
|
2132
|
-
content: [
|
|
2133
|
-
{ type: "text", text: JSON.stringify(repository, null, 2) },
|
|
2134
|
-
],
|
|
2239
|
+
content: [{ type: "text", text: JSON.stringify(repository, null, 2) }],
|
|
2135
2240
|
};
|
|
2136
2241
|
}
|
|
2137
2242
|
case "get_file_contents": {
|
|
@@ -2150,7 +2255,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2150
2255
|
}
|
|
2151
2256
|
case "push_files": {
|
|
2152
2257
|
const args = PushFilesSchema.parse(request.params.arguments);
|
|
2153
|
-
const result = await createCommit(args.project_id, args.commit_message, args.branch, args.files.map(
|
|
2258
|
+
const result = await createCommit(args.project_id, args.commit_message, args.branch, args.files.map(f => ({ path: f.file_path, content: f.content })));
|
|
2154
2259
|
return {
|
|
2155
2260
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
2156
2261
|
};
|
|
@@ -2168,9 +2273,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2168
2273
|
const { project_id, ...options } = args;
|
|
2169
2274
|
const mergeRequest = await createMergeRequest(project_id, options);
|
|
2170
2275
|
return {
|
|
2171
|
-
content: [
|
|
2172
|
-
{ type: "text", text: JSON.stringify(mergeRequest, null, 2) },
|
|
2173
|
-
],
|
|
2276
|
+
content: [{ type: "text", text: JSON.stringify(mergeRequest, null, 2) }],
|
|
2174
2277
|
};
|
|
2175
2278
|
}
|
|
2176
2279
|
case "update_merge_request_note": {
|
|
@@ -2207,9 +2310,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2207
2310
|
const args = GetMergeRequestSchema.parse(request.params.arguments);
|
|
2208
2311
|
const mergeRequest = await getMergeRequest(args.project_id, args.merge_request_iid, args.source_branch);
|
|
2209
2312
|
return {
|
|
2210
|
-
content: [
|
|
2211
|
-
{ type: "text", text: JSON.stringify(mergeRequest, null, 2) },
|
|
2212
|
-
],
|
|
2313
|
+
content: [{ type: "text", text: JSON.stringify(mergeRequest, null, 2) }],
|
|
2213
2314
|
};
|
|
2214
2315
|
}
|
|
2215
2316
|
case "get_merge_request_diffs": {
|
|
@@ -2224,18 +2325,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2224
2325
|
const { project_id, merge_request_iid, source_branch, ...options } = args;
|
|
2225
2326
|
const mergeRequest = await updateMergeRequest(project_id, options, merge_request_iid, source_branch);
|
|
2226
2327
|
return {
|
|
2227
|
-
content: [
|
|
2228
|
-
{ type: "text", text: JSON.stringify(mergeRequest, null, 2) },
|
|
2229
|
-
],
|
|
2328
|
+
content: [{ type: "text", text: JSON.stringify(mergeRequest, null, 2) }],
|
|
2230
2329
|
};
|
|
2231
2330
|
}
|
|
2232
2331
|
case "mr_discussions": {
|
|
2233
2332
|
const args = ListMergeRequestDiscussionsSchema.parse(request.params.arguments);
|
|
2234
2333
|
const discussions = await listMergeRequestDiscussions(args.project_id, args.merge_request_iid);
|
|
2235
2334
|
return {
|
|
2236
|
-
content: [
|
|
2237
|
-
{ type: "text", text: JSON.stringify(discussions, null, 2) },
|
|
2238
|
-
],
|
|
2335
|
+
content: [{ type: "text", text: JSON.stringify(discussions, null, 2) }],
|
|
2239
2336
|
};
|
|
2240
2337
|
}
|
|
2241
2338
|
case "list_namespaces": {
|
|
@@ -2260,9 +2357,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2260
2357
|
const data = await response.json();
|
|
2261
2358
|
const namespaces = z.array(GitLabNamespaceSchema).parse(data);
|
|
2262
2359
|
return {
|
|
2263
|
-
content: [
|
|
2264
|
-
{ type: "text", text: JSON.stringify(namespaces, null, 2) },
|
|
2265
|
-
],
|
|
2360
|
+
content: [{ type: "text", text: JSON.stringify(namespaces, null, 2) }],
|
|
2266
2361
|
};
|
|
2267
2362
|
}
|
|
2268
2363
|
case "get_namespace": {
|
|
@@ -2288,9 +2383,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2288
2383
|
const data = await response.json();
|
|
2289
2384
|
const namespaceExists = GitLabNamespaceExistsResponseSchema.parse(data);
|
|
2290
2385
|
return {
|
|
2291
|
-
content: [
|
|
2292
|
-
{ type: "text", text: JSON.stringify(namespaceExists, null, 2) },
|
|
2293
|
-
],
|
|
2386
|
+
content: [{ type: "text", text: JSON.stringify(namespaceExists, null, 2) }],
|
|
2294
2387
|
};
|
|
2295
2388
|
}
|
|
2296
2389
|
case "get_project": {
|
|
@@ -2376,9 +2469,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2376
2469
|
const { project_id, issue_iid, ...options } = args;
|
|
2377
2470
|
const discussions = await listIssueDiscussions(project_id, issue_iid, options);
|
|
2378
2471
|
return {
|
|
2379
|
-
content: [
|
|
2380
|
-
{ type: "text", text: JSON.stringify(discussions, null, 2) },
|
|
2381
|
-
],
|
|
2472
|
+
content: [{ type: "text", text: JSON.stringify(discussions, null, 2) }],
|
|
2382
2473
|
};
|
|
2383
2474
|
}
|
|
2384
2475
|
case "get_issue_link": {
|
|
@@ -2564,13 +2655,47 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2564
2655
|
],
|
|
2565
2656
|
};
|
|
2566
2657
|
}
|
|
2658
|
+
case "create_pipeline": {
|
|
2659
|
+
const { project_id, ref, variables } = CreatePipelineSchema.parse(request.params.arguments);
|
|
2660
|
+
const pipeline = await createPipeline(project_id, ref, variables);
|
|
2661
|
+
return {
|
|
2662
|
+
content: [
|
|
2663
|
+
{
|
|
2664
|
+
type: "text",
|
|
2665
|
+
text: `Created pipeline #${pipeline.id} for ${ref}. Status: ${pipeline.status}\nWeb URL: ${pipeline.web_url}`,
|
|
2666
|
+
},
|
|
2667
|
+
],
|
|
2668
|
+
};
|
|
2669
|
+
}
|
|
2670
|
+
case "retry_pipeline": {
|
|
2671
|
+
const { project_id, pipeline_id } = RetryPipelineSchema.parse(request.params.arguments);
|
|
2672
|
+
const pipeline = await retryPipeline(project_id, pipeline_id);
|
|
2673
|
+
return {
|
|
2674
|
+
content: [
|
|
2675
|
+
{
|
|
2676
|
+
type: "text",
|
|
2677
|
+
text: `Retried pipeline #${pipeline.id}. Status: ${pipeline.status}\nWeb URL: ${pipeline.web_url}`,
|
|
2678
|
+
},
|
|
2679
|
+
],
|
|
2680
|
+
};
|
|
2681
|
+
}
|
|
2682
|
+
case "cancel_pipeline": {
|
|
2683
|
+
const { project_id, pipeline_id } = CancelPipelineSchema.parse(request.params.arguments);
|
|
2684
|
+
const pipeline = await cancelPipeline(project_id, pipeline_id);
|
|
2685
|
+
return {
|
|
2686
|
+
content: [
|
|
2687
|
+
{
|
|
2688
|
+
type: "text",
|
|
2689
|
+
text: `Canceled pipeline #${pipeline.id}. Status: ${pipeline.status}\nWeb URL: ${pipeline.web_url}`,
|
|
2690
|
+
},
|
|
2691
|
+
],
|
|
2692
|
+
};
|
|
2693
|
+
}
|
|
2567
2694
|
case "list_merge_requests": {
|
|
2568
2695
|
const args = ListMergeRequestsSchema.parse(request.params.arguments);
|
|
2569
2696
|
const mergeRequests = await listMergeRequests(args.project_id, args);
|
|
2570
2697
|
return {
|
|
2571
|
-
content: [
|
|
2572
|
-
{ type: "text", text: JSON.stringify(mergeRequests, null, 2) },
|
|
2573
|
-
],
|
|
2698
|
+
content: [{ type: "text", text: JSON.stringify(mergeRequests, null, 2) }],
|
|
2574
2699
|
};
|
|
2575
2700
|
}
|
|
2576
2701
|
case "list_milestones": {
|
|
@@ -2691,7 +2816,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
2691
2816
|
catch (error) {
|
|
2692
2817
|
if (error instanceof z.ZodError) {
|
|
2693
2818
|
throw new Error(`Invalid arguments: ${error.errors
|
|
2694
|
-
.map(
|
|
2819
|
+
.map(e => `${e.path.join(".")}: ${e.message}`)
|
|
2695
2820
|
.join(", ")}`);
|
|
2696
2821
|
}
|
|
2697
2822
|
throw error;
|
|
@@ -2716,7 +2841,7 @@ async function runServer() {
|
|
|
2716
2841
|
process.exit(1);
|
|
2717
2842
|
}
|
|
2718
2843
|
}
|
|
2719
|
-
runServer().catch(
|
|
2844
|
+
runServer().catch(error => {
|
|
2720
2845
|
console.error("Fatal error in main():", error);
|
|
2721
2846
|
process.exit(1);
|
|
2722
2847
|
});
|