@zereight/mcp-gitlab 2.0.4 → 2.0.6

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.
@@ -0,0 +1,381 @@
1
+ #!/usr/bin/env ts-node
2
+ /**
3
+ * 읽기 전용 GitLab MCP 도구 테스트 스크립트 (TypeScript)
4
+ * 읽기 전용 GitLab MCP 도구들을 자동으로 테스트합니다.
5
+ */
6
+ import { spawn } from 'child_process';
7
+ import * as fs from 'fs';
8
+ import * as dotenv from 'dotenv';
9
+ // .env 파일 로드
10
+ dotenv.config();
11
+ // 환경 변수 설정
12
+ const GITLAB_API_URL = process.env.GITLAB_API_URL || "https://gitlab.com/api/v4";
13
+ const GITLAB_TOKEN = process.env.GITLAB_PERSONAL_ACCESS_TOKEN || process.env.GITLAB_TOKEN;
14
+ const TEST_PROJECT_ID = process.env.GITLAB_PROJECT_ID || process.env.TEST_PROJECT_ID;
15
+ // 테스트 결과 저장
16
+ const testResults = {
17
+ passed: 0,
18
+ failed: 0,
19
+ skipped: 0,
20
+ total: 0,
21
+ details: []
22
+ };
23
+ // 테스트 도구 목록 (GitLab MCP에서 제공하는 모든 도구들)
24
+ const mcpTools = [
25
+ // 프로젝트 관련
26
+ { name: 'list_projects', category: 'project', required: false },
27
+ { name: 'search_repositories', category: 'project', required: false },
28
+ { name: 'get_project', category: 'project', required: true },
29
+ { name: 'list_project_members', category: 'project', required: true },
30
+ { name: 'list_group_projects', category: 'project', required: false },
31
+ // 이슈 관련
32
+ { name: 'list_issues', category: 'issue', required: true },
33
+ { name: 'my_issues', category: 'issue', required: false },
34
+ { name: 'get_issue', category: 'issue', required: true },
35
+ { name: 'list_issue_discussions', category: 'issue', required: true },
36
+ { name: 'list_issue_links', category: 'issue', required: true },
37
+ // 머지 리퀘스트 관련
38
+ { name: 'list_merge_requests', category: 'merge_request', required: true },
39
+ { name: 'get_merge_request', category: 'merge_request', required: true },
40
+ { name: 'get_merge_request_diffs', category: 'merge_request', required: true },
41
+ { name: 'list_merge_request_diffs', category: 'merge_request', required: true },
42
+ { name: 'get_branch_diffs', category: 'merge_request', required: true },
43
+ { name: 'mr_discussions', category: 'merge_request', required: true },
44
+ // 파이프라인 관련
45
+ { name: 'list_pipelines', category: 'pipeline', required: true },
46
+ { name: 'get_pipeline', category: 'pipeline', required: true },
47
+ { name: 'list_pipeline_jobs', category: 'pipeline', required: true },
48
+ { name: 'list_pipeline_trigger_jobs', category: 'pipeline', required: true },
49
+ { name: 'get_pipeline_job', category: 'pipeline', required: true },
50
+ { name: 'get_pipeline_job_output', category: 'pipeline', required: true },
51
+ // 파일 관리
52
+ { name: 'get_file_contents', category: 'file', required: true },
53
+ { name: 'get_repository_tree', category: 'file', required: true },
54
+ // 커밋 관련
55
+ { name: 'list_commits', category: 'commit', required: true },
56
+ { name: 'get_commit', category: 'commit', required: true },
57
+ { name: 'get_commit_diff', category: 'commit', required: true },
58
+ // 라벨 관련
59
+ { name: 'list_labels', category: 'label', required: true },
60
+ { name: 'get_label', category: 'label', required: true },
61
+ // 네임스페이스 관련
62
+ { name: 'list_namespaces', category: 'namespace', required: false },
63
+ { name: 'get_namespace', category: 'namespace', required: false },
64
+ { name: 'verify_namespace', category: 'namespace', required: false },
65
+ // 사용자 관련
66
+ { name: 'get_users', category: 'user', required: false },
67
+ // 이벤트 관련
68
+ { name: 'list_events', category: 'event', required: false },
69
+ { name: 'get_project_events', category: 'event', required: true },
70
+ // 마일스톤 관련 (선택적)
71
+ { name: 'list_milestones', category: 'milestone', required: true },
72
+ { name: 'get_milestone', category: 'milestone', required: true },
73
+ // 위키 관련 (선택적)
74
+ { name: 'list_wiki_pages', category: 'wiki', required: true },
75
+ { name: 'get_wiki_page', category: 'wiki', required: true },
76
+ // 그룹 이터레이션 관련
77
+ { name: 'list_group_iterations', category: 'iteration', required: false }
78
+ ];
79
+ // MCP 응답 파싱 함수
80
+ function parseMCPResponse(output) {
81
+ const lines = output.trim().split('\n');
82
+ for (const line of lines) {
83
+ if (line.startsWith('{')) {
84
+ try {
85
+ const result = JSON.parse(line);
86
+ if (result.result !== undefined) {
87
+ return result.result;
88
+ }
89
+ }
90
+ catch (e) {
91
+ // JSON 파싱 실패 시 계속 시도
92
+ }
93
+ }
94
+ }
95
+ return { success: true, raw: output };
96
+ }
97
+ // MCP 서버와 통신하는 함수
98
+ async function callMCPTool(toolName, parameters = {}) {
99
+ return new Promise((resolve, reject) => {
100
+ const mcpProcess = spawn('node', ['build/index.js'], {
101
+ stdio: ['pipe', 'pipe', 'pipe'],
102
+ env: {
103
+ ...process.env,
104
+ GITLAB_PERSONAL_ACCESS_TOKEN: GITLAB_TOKEN,
105
+ GITLAB_API_URL: GITLAB_API_URL,
106
+ GITLAB_PROJECT_ID: TEST_PROJECT_ID
107
+ }
108
+ });
109
+ let output = '';
110
+ let errorOutput = '';
111
+ mcpProcess.stdout?.on('data', (data) => {
112
+ output += data.toString();
113
+ });
114
+ mcpProcess.stderr?.on('data', (data) => {
115
+ errorOutput += data.toString();
116
+ });
117
+ mcpProcess.on('close', (code) => {
118
+ if (code === 0) {
119
+ try {
120
+ const result = parseMCPResponse(output);
121
+ resolve(result);
122
+ }
123
+ catch (e) {
124
+ reject(new Error(`MCP 응답 파싱 실패: ${e.message}`));
125
+ }
126
+ }
127
+ else {
128
+ reject(new Error(`MCP 프로세스 종료 코드 ${code}: ${errorOutput}`));
129
+ }
130
+ });
131
+ // MCP 요청 전송
132
+ const request = {
133
+ jsonrpc: "2.0",
134
+ id: 1,
135
+ method: "tools/call",
136
+ params: {
137
+ name: toolName,
138
+ arguments: parameters
139
+ }
140
+ };
141
+ mcpProcess.stdin?.write(JSON.stringify(request) + '\n');
142
+ mcpProcess.stdin?.end();
143
+ });
144
+ }
145
+ // 도구별 파라미터 설정 함수
146
+ async function setupToolParameters(tool) {
147
+ let parameters = {};
148
+ if (tool.required && TEST_PROJECT_ID) {
149
+ parameters.project_id = TEST_PROJECT_ID;
150
+ }
151
+ // 특정 도구들의 추가 파라미터 설정
152
+ switch (tool.name) {
153
+ case 'get_issue':
154
+ if (TEST_PROJECT_ID) {
155
+ const listResult = await callMCPTool('list_issues', parameters);
156
+ if (Array.isArray(listResult) && listResult.length > 0) {
157
+ parameters.issue_iid = listResult[0].iid;
158
+ }
159
+ else {
160
+ throw new Error('No issues found to test with');
161
+ }
162
+ }
163
+ break;
164
+ case 'get_merge_request':
165
+ if (TEST_PROJECT_ID) {
166
+ const listResult = await callMCPTool('list_merge_requests', parameters);
167
+ if (Array.isArray(listResult) && listResult.length > 0) {
168
+ parameters.merge_request_iid = listResult[0].iid;
169
+ }
170
+ else {
171
+ throw new Error('No merge requests found to test with');
172
+ }
173
+ }
174
+ break;
175
+ case 'get_pipeline':
176
+ if (TEST_PROJECT_ID) {
177
+ const pipelinesResult = await callMCPTool('list_pipelines', parameters);
178
+ if (Array.isArray(pipelinesResult) && pipelinesResult.length > 0) {
179
+ parameters.pipeline_id = pipelinesResult[0].id;
180
+ }
181
+ else {
182
+ throw new Error('No pipelines found to test with');
183
+ }
184
+ }
185
+ break;
186
+ case 'get_pipeline_job':
187
+ if (TEST_PROJECT_ID) {
188
+ const pipelinesResult = await callMCPTool('list_pipelines', parameters);
189
+ if (Array.isArray(pipelinesResult) && pipelinesResult.length > 0) {
190
+ parameters.pipeline_id = pipelinesResult[0].id;
191
+ const jobsResult = await callMCPTool('list_pipeline_jobs', parameters);
192
+ if (Array.isArray(jobsResult) && jobsResult.length > 0) {
193
+ parameters.job_id = jobsResult[0].id;
194
+ }
195
+ }
196
+ else {
197
+ throw new Error('No pipelines found to test with');
198
+ }
199
+ }
200
+ break;
201
+ case 'get_commit':
202
+ if (TEST_PROJECT_ID) {
203
+ const commitsResult = await callMCPTool('list_commits', parameters);
204
+ if (Array.isArray(commitsResult) && commitsResult.length > 0) {
205
+ parameters.sha = commitsResult[0].id;
206
+ }
207
+ else {
208
+ throw new Error('No commits found to test with');
209
+ }
210
+ }
211
+ break;
212
+ case 'get_label':
213
+ if (TEST_PROJECT_ID) {
214
+ const labelsResult = await callMCPTool('list_labels', parameters);
215
+ if (Array.isArray(labelsResult) && labelsResult.length > 0) {
216
+ parameters.label_id = labelsResult[0].id;
217
+ }
218
+ else {
219
+ throw new Error('No labels found to test with');
220
+ }
221
+ }
222
+ break;
223
+ case 'get_wiki_page':
224
+ if (TEST_PROJECT_ID) {
225
+ const wikiResult = await callMCPTool('list_wiki_pages', parameters);
226
+ if (Array.isArray(wikiResult) && wikiResult.length > 0) {
227
+ parameters.slug = wikiResult[0].slug;
228
+ }
229
+ else {
230
+ throw new Error('No wiki pages found to test with');
231
+ }
232
+ }
233
+ break;
234
+ case 'get_milestone':
235
+ if (TEST_PROJECT_ID) {
236
+ const milestonesResult = await callMCPTool('list_milestones', parameters);
237
+ if (Array.isArray(milestonesResult) && milestonesResult.length > 0) {
238
+ parameters.milestone_id = milestonesResult[0].id;
239
+ }
240
+ else {
241
+ throw new Error('No milestones found to test with');
242
+ }
243
+ }
244
+ break;
245
+ case 'search_repositories':
246
+ parameters.search = 'test';
247
+ break;
248
+ case 'get_users':
249
+ parameters.usernames = ['root'];
250
+ break;
251
+ case 'verify_namespace':
252
+ parameters.path = 'root';
253
+ break;
254
+ }
255
+ return parameters;
256
+ }
257
+ // 개별 도구 테스트 함수
258
+ async function testTool(tool) {
259
+ const startTime = Date.now();
260
+ let result = {
261
+ name: tool.name,
262
+ category: tool.category,
263
+ status: 'skipped',
264
+ duration: 0,
265
+ response: null
266
+ };
267
+ try {
268
+ console.log(`🧪 Testing ${tool.name}...`);
269
+ const parameters = await setupToolParameters(tool);
270
+ const response = await callMCPTool(tool.name, parameters);
271
+ result.response = response;
272
+ result.status = 'passed';
273
+ result.duration = Date.now() - startTime;
274
+ console.log(`✅ ${tool.name} - PASSED (${result.duration}ms)`);
275
+ }
276
+ catch (error) {
277
+ result.status = 'failed';
278
+ result.error = error.message;
279
+ result.duration = Date.now() - startTime;
280
+ console.log(`❌ ${tool.name} - FAILED (${result.duration}ms)`);
281
+ console.log(` Error: ${error.message}`);
282
+ }
283
+ return result;
284
+ }
285
+ // 메인 테스트 실행 함수
286
+ async function runReadOnlyTests() {
287
+ console.log('🚀 GitLab MCP 읽기 전용 도구 테스트 시작\n');
288
+ console.log(`📊 총 ${mcpTools.length}개의 읽기 전용 도구를 테스트합니다.\n`);
289
+ // 환경 변수 확인
290
+ if (!GITLAB_TOKEN) {
291
+ console.error('❌ GITLAB_PERSONAL_ACCESS_TOKEN 환경 변수가 설정되지 않았습니다.');
292
+ process.exit(1);
293
+ }
294
+ if (!TEST_PROJECT_ID) {
295
+ console.warn('⚠️ GITLAB_PROJECT_ID가 설정되지 않았습니다. 일부 테스트가 건너뛰어질 수 있습니다.');
296
+ }
297
+ // MCP 서버 빌드 확인
298
+ if (!fs.existsSync('build/index.js')) {
299
+ console.log('🔨 MCP 서버를 빌드합니다...');
300
+ const buildProcess = spawn('npm', ['run', 'build'], { stdio: 'inherit' });
301
+ await new Promise((resolve, reject) => {
302
+ buildProcess.on('close', (code) => {
303
+ if (code === 0)
304
+ resolve();
305
+ else
306
+ reject(new Error(`Build failed with code ${code}`));
307
+ });
308
+ });
309
+ }
310
+ // 각 도구 테스트 실행
311
+ for (const tool of mcpTools) {
312
+ const result = await testTool(tool);
313
+ testResults.details.push(result);
314
+ testResults.total++;
315
+ if (result.status === 'passed') {
316
+ testResults.passed++;
317
+ }
318
+ else if (result.status === 'failed') {
319
+ testResults.failed++;
320
+ }
321
+ else {
322
+ testResults.skipped++;
323
+ }
324
+ // 요청 간 간격 (API 제한 방지)
325
+ await new Promise(resolve => setTimeout(resolve, 100));
326
+ }
327
+ // 결과 출력
328
+ console.log('\n📊 테스트 결과 요약');
329
+ console.log('='.repeat(50));
330
+ console.log(`총 테스트: ${testResults.total}`);
331
+ console.log(`✅ 성공: ${testResults.passed}`);
332
+ console.log(`❌ 실패: ${testResults.failed}`);
333
+ console.log(`⏭️ 건너뜀: ${testResults.skipped}`);
334
+ console.log(`성공률: ${((testResults.passed / testResults.total) * 100).toFixed(1)}%`);
335
+ // 카테고리별 결과
336
+ const categoryResults = {};
337
+ testResults.details.forEach(result => {
338
+ if (!categoryResults[result.category]) {
339
+ categoryResults[result.category] = { passed: 0, failed: 0, total: 0 };
340
+ }
341
+ categoryResults[result.category].total++;
342
+ if (result.status === 'passed') {
343
+ categoryResults[result.category].passed++;
344
+ }
345
+ else if (result.status === 'failed') {
346
+ categoryResults[result.category].failed++;
347
+ }
348
+ });
349
+ console.log('\n📈 카테고리별 결과');
350
+ console.log('-'.repeat(30));
351
+ Object.entries(categoryResults).forEach(([category, stats]) => {
352
+ const successRate = ((stats.passed / stats.total) * 100).toFixed(1);
353
+ console.log(`${category.padEnd(15)}: ${stats.passed}/${stats.total} (${successRate}%)`);
354
+ });
355
+ // 실패한 테스트 상세 정보
356
+ const failedTests = testResults.details.filter(r => r.status === 'failed');
357
+ if (failedTests.length > 0) {
358
+ console.log('\n❌ 실패한 테스트 상세 정보');
359
+ console.log('-'.repeat(40));
360
+ failedTests.forEach(test => {
361
+ console.log(`${test.name}: ${test.error}`);
362
+ });
363
+ }
364
+ // 결과를 JSON 파일로 저장
365
+ const reportPath = 'test-results-readonly.json';
366
+ fs.writeFileSync(reportPath, JSON.stringify(testResults, null, 2));
367
+ console.log(`\n📄 상세 결과가 ${reportPath}에 저장되었습니다.`);
368
+ return testResults.failed === 0;
369
+ }
370
+ // 테스트 실행
371
+ if (import.meta.url === `file://${process.argv[1]}`) {
372
+ runReadOnlyTests()
373
+ .then(success => {
374
+ process.exit(success ? 0 : 1);
375
+ })
376
+ .catch(error => {
377
+ console.error('테스트 실행 중 오류 발생:', error);
378
+ process.exit(1);
379
+ });
380
+ }
381
+ export { runReadOnlyTests, mcpTools, testResults };
@@ -0,0 +1,190 @@
1
+ #!/usr/bin/env ts-node
2
+ /**
3
+ * 간단한 GitLab MCP 도구 테스트 스크립트 (TypeScript)
4
+ * MCP 서버를 직접 호출하여 기본 도구들을 테스트합니다.
5
+ */
6
+ import { spawn } from 'child_process';
7
+ import * as fs from 'fs';
8
+ // 환경 변수
9
+ const GITLAB_TOKEN = process.env.GITLAB_PERSONAL_ACCESS_TOKEN || process.env.GITLAB_TOKEN;
10
+ const GITLAB_API_URL = process.env.GITLAB_API_URL || "https://gitlab.com/api/v4";
11
+ const TEST_PROJECT_ID = process.env.GITLAB_PROJECT_ID || process.env.TEST_PROJECT_ID;
12
+ // 테스트 결과
13
+ const results = {
14
+ passed: 0,
15
+ failed: 0,
16
+ total: 0,
17
+ tests: []
18
+ };
19
+ // MCP 응답 파싱 함수
20
+ function parseMCPResponse(output) {
21
+ const lines = output.trim().split('\n');
22
+ for (const line of lines) {
23
+ if (line.startsWith('{')) {
24
+ try {
25
+ const result = JSON.parse(line);
26
+ if (result.result !== undefined) {
27
+ return result.result;
28
+ }
29
+ }
30
+ catch (e) {
31
+ // JSON 파싱 실패 시 계속 시도
32
+ }
33
+ }
34
+ }
35
+ return { success: true, raw: output };
36
+ }
37
+ // MCP 서버와 통신하는 함수
38
+ function callMCPTool(toolName, parameters = {}) {
39
+ return new Promise((resolve, reject) => {
40
+ const mcpProcess = spawn('node', ['build/index.js'], {
41
+ stdio: ['pipe', 'pipe', 'pipe'],
42
+ env: {
43
+ ...process.env,
44
+ GITLAB_PERSONAL_ACCESS_TOKEN: GITLAB_TOKEN,
45
+ GITLAB_API_URL: GITLAB_API_URL,
46
+ GITLAB_PROJECT_ID: TEST_PROJECT_ID
47
+ }
48
+ });
49
+ let output = '';
50
+ let errorOutput = '';
51
+ mcpProcess.stdout?.on('data', (data) => {
52
+ output += data.toString();
53
+ });
54
+ mcpProcess.stderr?.on('data', (data) => {
55
+ errorOutput += data.toString();
56
+ });
57
+ mcpProcess.on('close', (code) => {
58
+ if (code === 0) {
59
+ try {
60
+ const result = parseMCPResponse(output);
61
+ resolve(result);
62
+ }
63
+ catch (e) {
64
+ reject(new Error(`응답 파싱 실패: ${e.message}`));
65
+ }
66
+ }
67
+ else {
68
+ reject(new Error(`MCP 프로세스 종료 코드 ${code}: ${errorOutput}`));
69
+ }
70
+ });
71
+ // MCP 요청 전송
72
+ const request = {
73
+ jsonrpc: "2.0",
74
+ id: 1,
75
+ method: "tools/call",
76
+ params: {
77
+ name: toolName,
78
+ arguments: parameters
79
+ }
80
+ };
81
+ mcpProcess.stdin?.write(JSON.stringify(request) + '\n');
82
+ mcpProcess.stdin?.end();
83
+ });
84
+ }
85
+ // 개별 테스트 실행
86
+ async function runTest(testName, toolName, parameters = {}) {
87
+ const startTime = Date.now();
88
+ console.log(`🧪 ${testName} 테스트 중...`);
89
+ try {
90
+ const result = await callMCPTool(toolName, parameters);
91
+ const duration = Date.now() - startTime;
92
+ console.log(`✅ ${testName} - 성공 (${duration}ms)`);
93
+ results.passed++;
94
+ results.tests.push({
95
+ name: testName,
96
+ tool: toolName,
97
+ status: 'passed',
98
+ duration,
99
+ result: typeof result === 'object' ? 'object' : typeof result
100
+ });
101
+ }
102
+ catch (error) {
103
+ const duration = Date.now() - startTime;
104
+ console.log(`❌ ${testName} - 실패 (${duration}ms): ${error.message}`);
105
+ results.failed++;
106
+ results.tests.push({
107
+ name: testName,
108
+ tool: toolName,
109
+ status: 'failed',
110
+ duration,
111
+ error: error.message
112
+ });
113
+ }
114
+ results.total++;
115
+ }
116
+ // 메인 테스트 함수
117
+ async function runTests() {
118
+ console.log('🚀 GitLab MCP 도구 테스트 시작\n');
119
+ // 환경 변수 확인
120
+ if (!GITLAB_TOKEN) {
121
+ console.error('❌ GITLAB_PERSONAL_ACCESS_TOKEN 환경 변수가 필요합니다.');
122
+ process.exit(1);
123
+ }
124
+ if (!TEST_PROJECT_ID) {
125
+ console.warn('⚠️ GITLAB_PROJECT_ID가 설정되지 않았습니다. 일부 테스트가 건너뛰어질 수 있습니다.');
126
+ }
127
+ // MCP 서버 빌드 확인
128
+ if (!fs.existsSync('build/index.js')) {
129
+ console.log('🔨 MCP 서버를 빌드합니다...');
130
+ const buildProcess = spawn('npm', ['run', 'build'], { stdio: 'inherit' });
131
+ await new Promise((resolve, reject) => {
132
+ buildProcess.on('close', (code) => {
133
+ if (code === 0)
134
+ resolve();
135
+ else
136
+ reject(new Error(`빌드 실패: ${code}`));
137
+ });
138
+ });
139
+ }
140
+ console.log('📋 기본 테스트 실행 중...\n');
141
+ // 기본 읽기 전용 테스트들
142
+ await runTest('프로젝트 목록 조회', 'list_projects', { per_page: 5 });
143
+ await runTest('프로젝트 검색', 'search_repositories', { search: 'gitlab' });
144
+ if (TEST_PROJECT_ID) {
145
+ await runTest('프로젝트 정보 조회', 'get_project', { project_id: TEST_PROJECT_ID });
146
+ await runTest('프로젝트 멤버 목록', 'list_project_members', { project_id: TEST_PROJECT_ID });
147
+ await runTest('이슈 목록 조회', 'list_issues', { project_id: TEST_PROJECT_ID, per_page: 5 });
148
+ await runTest('머지 리퀘스트 목록', 'list_merge_requests', { project_id: TEST_PROJECT_ID, per_page: 5 });
149
+ await runTest('파이프라인 목록', 'list_pipelines', { project_id: TEST_PROJECT_ID, per_page: 5 });
150
+ await runTest('커밋 목록', 'list_commits', { project_id: TEST_PROJECT_ID, per_page: 5 });
151
+ await runTest('라벨 목록', 'list_labels', { project_id: TEST_PROJECT_ID });
152
+ await runTest('파일 트리 조회', 'get_repository_tree', { project_id: TEST_PROJECT_ID });
153
+ await runTest('프로젝트 이벤트', 'get_project_events', { project_id: TEST_PROJECT_ID, per_page: 5 });
154
+ }
155
+ // 네임스페이스 관련 테스트
156
+ await runTest('네임스페이스 목록', 'list_namespaces', { per_page: 5 });
157
+ await runTest('사용자 정보 조회', 'get_users', { usernames: ['root'] });
158
+ await runTest('전체 이벤트 조회', 'list_events', { per_page: 5 });
159
+ // 결과 출력
160
+ console.log('\n📊 테스트 결과');
161
+ console.log('='.repeat(40));
162
+ console.log(`총 테스트: ${results.total}`);
163
+ console.log(`✅ 성공: ${results.passed}`);
164
+ console.log(`❌ 실패: ${results.failed}`);
165
+ console.log(`성공률: ${((results.passed / results.total) * 100).toFixed(1)}%`);
166
+ // 실패한 테스트 상세 정보
167
+ const failedTests = results.tests.filter(t => t.status === 'failed');
168
+ if (failedTests.length > 0) {
169
+ console.log('\n❌ 실패한 테스트:');
170
+ failedTests.forEach(test => {
171
+ console.log(` - ${test.name}: ${test.error}`);
172
+ });
173
+ }
174
+ // 결과를 파일로 저장
175
+ fs.writeFileSync('test-results-simple.json', JSON.stringify(results, null, 2));
176
+ console.log('\n📄 상세 결과가 test-results-simple.json에 저장되었습니다.');
177
+ return results.failed === 0;
178
+ }
179
+ // 실행
180
+ if (import.meta.url === `file://${process.argv[1]}`) {
181
+ runTests()
182
+ .then(success => {
183
+ process.exit(success ? 0 : 1);
184
+ })
185
+ .catch(error => {
186
+ console.error('테스트 실행 오류:', error);
187
+ process.exit(1);
188
+ });
189
+ }
190
+ export { runTests, callMCPTool };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zereight/mcp-gitlab",
3
- "version": "2.0.4",
3
+ "version": "2.0.6",
4
4
  "description": "MCP server for using the GitLab API",
5
5
  "license": "MIT",
6
6
  "author": "zereight",
@@ -25,6 +25,8 @@
25
25
  "test": "node test/validate-api.js",
26
26
  "test:integration": "node test/validate-api.js",
27
27
  "test:server": "npm run build && node build/test/test-all-transport-server.js",
28
+ "test:mcp:readonly": "tsx test/readonly-mcp-tests.ts",
29
+ "test:all": "npm run test && npm run test:mcp:readonly",
28
30
  "lint": "eslint . --ext .ts",
29
31
  "lint:fix": "eslint . --ext .ts --fix",
30
32
  "format": "prettier --write \"**/*.{js,ts,json,md}\"",
@@ -51,9 +53,11 @@
51
53
  "@typescript-eslint/eslint-plugin": "^8.21.0",
52
54
  "@typescript-eslint/parser": "^8.21.0",
53
55
  "auto-changelog": "^2.4.0",
56
+ "dotenv": "^17.2.2",
54
57
  "eslint": "^9.18.0",
55
58
  "prettier": "^3.4.2",
56
59
  "ts-node": "^10.9.2",
60
+ "tsx": "^4.20.5",
57
61
  "typescript": "^5.8.2",
58
62
  "zod": "^3.24.2"
59
63
  }