From 1926d77c2f90e9a16528f1c9dc4aee57d166153b Mon Sep 17 00:00:00 2001 From: Admin Date: Mon, 17 Mar 2025 13:47:20 -0700 Subject: [PATCH 1/8] Fix: Use plural resource names in createNote URL construction - Changed to s to use the correct plural form in API endpoints - This fixes 404 errors when trying to add notes to issues or merge requests - Added a test file to verify the fix - Conforms to GitLab API documentation which uses plural resource names (issues, merge_requests) --- index.ts | 2 +- test-note.ts | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 test-note.ts diff --git a/index.ts b/index.ts index 17f6751..3087cc2 100644 --- a/index.ts +++ b/index.ts @@ -585,7 +585,7 @@ async function createNote( const url = new URL( `${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent( projectId - )}/${noteableType}/${noteableIid}/notes` + )}/${noteableType}s/${noteableIid}/notes` // Using plural form (issues/merge_requests) as per GitLab API documentation ); const response = await fetch(url.toString(), { diff --git a/test-note.ts b/test-note.ts new file mode 100644 index 0000000..25504aa --- /dev/null +++ b/test-note.ts @@ -0,0 +1,66 @@ +/** + * This test file verifies that the createNote function works correctly + * with the fixed endpoint URL construction that uses plural resource names + * (issues instead of issue, merge_requests instead of merge_request). + */ + +import fetch from "node-fetch"; + +// GitLab API configuration (replace with actual values when testing) +const GITLAB_API_URL = process.env.GITLAB_API_URL || "https://gitlab.com"; +const GITLAB_PERSONAL_ACCESS_TOKEN = process.env.GITLAB_TOKEN || ""; +const PROJECT_ID = process.env.PROJECT_ID || "your/project"; +const ISSUE_IID = Number(process.env.ISSUE_IID || "1"); + +async function testCreateIssueNote() { + try { + // Using plural form "issues" in the URL + const url = new URL( + `${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent( + PROJECT_ID + )}/issues/${ISSUE_IID}/notes` + ); + + const response = await fetch(url.toString(), { + method: "POST", + headers: { + Accept: "application/json", + "Content-Type": "application/json", + Authorization: `Bearer ${GITLAB_PERSONAL_ACCESS_TOKEN}`, + }, + body: JSON.stringify({ body: "Test note from API - with plural endpoint" }), + }); + + if (!response.ok) { + const errorBody = await response.text(); + throw new Error( + `GitLab API error: ${response.status} ${response.statusText}\n${errorBody}` + ); + } + + const data = await response.json(); + console.log("Successfully created note:"); + console.log(JSON.stringify(data, null, 2)); + return true; + } catch (error) { + console.error("Error creating note:", error); + return false; + } +} + +// Only run the test if executed directly +if (require.main === module) { + console.log("Testing note creation with plural 'issues' endpoint..."); + testCreateIssueNote().then(success => { + if (success) { + console.log("✅ Test successful!"); + process.exit(0); + } else { + console.log("❌ Test failed!"); + process.exit(1); + } + }); +} + +// Export for use in other tests +export { testCreateIssueNote }; From 9c17e9b828a575917491d50c2f183f2edad0155f Mon Sep 17 00:00:00 2001 From: Admin Date: Mon, 17 Mar 2025 14:10:47 -0700 Subject: [PATCH 2/8] Fix create_note API URL construction to avoid duplicate /api/v4 path --- index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.ts b/index.ts index 3087cc2..ad1dbeb 100644 --- a/index.ts +++ b/index.ts @@ -583,7 +583,7 @@ async function createNote( ): Promise { // ⚙️ 응답 타입은 GitLab API 문서에 따라 조정 가능 const url = new URL( - `${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent( + `${GITLAB_API_URL}/projects/${encodeURIComponent( projectId )}/${noteableType}s/${noteableIid}/notes` // Using plural form (issues/merge_requests) as per GitLab API documentation ); From 68c332c0a7bf44b09a6c41656900be2ba697101a Mon Sep 17 00:00:00 2001 From: Admin Date: Mon, 17 Mar 2025 14:13:45 -0700 Subject: [PATCH 3/8] Update version to 1.0.7-fix to reflect URL path fix --- index.ts | 2 +- package.json | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/index.ts b/index.ts index ad1dbeb..d499d61 100644 --- a/index.ts +++ b/index.ts @@ -57,7 +57,7 @@ import { const server = new Server( { name: "better-gitlab-mcp-server", - version: "0.0.1", + version: "1.0.7-fix", }, { capabilities: { diff --git a/package.json b/package.json index 384e37d..e2a3146 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@zereight/mcp-gitlab", - "version": "1.0.6", + "version": "1.0.7-fix", "description": "MCP server for using the GitLab API", "license": "MIT", "author": "zereight", @@ -27,6 +27,8 @@ "zod-to-json-schema": "^3.23.5" }, "devDependencies": { - "typescript": "^5.6.2" + "@types/node": "^22.13.10", + "typescript": "^5.8.2", + "zod": "3.21.4" } } From 51641d7b9b9caab50a2c8eeda8da1d2a529e90d0 Mon Sep 17 00:00:00 2001 From: Admin Date: Mon, 17 Mar 2025 14:14:48 -0700 Subject: [PATCH 4/8] Add debug logging to createNote function --- build/index.js | 8 ++++++-- index.ts | 4 ++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/build/index.js b/build/index.js index 7baefac..ef785bf 100755 --- a/build/index.js +++ b/build/index.js @@ -8,7 +8,7 @@ import { zodToJsonSchema } from "zod-to-json-schema"; import { GitLabForkSchema, GitLabReferenceSchema, GitLabRepositorySchema, GitLabIssueSchema, GitLabMergeRequestSchema, GitLabContentSchema, GitLabCreateUpdateFileResponseSchema, GitLabSearchResponseSchema, GitLabTreeSchema, GitLabCommitSchema, CreateOrUpdateFileSchema, SearchRepositoriesSchema, CreateRepositorySchema, GetFileContentsSchema, PushFilesSchema, CreateIssueSchema, CreateMergeRequestSchema, ForkRepositorySchema, CreateBranchSchema, GitLabMergeRequestDiffSchema, GetMergeRequestSchema, GetMergeRequestDiffsSchema, UpdateMergeRequestSchema, CreateNoteSchema, } from "./schemas.js"; const server = new Server({ name: "better-gitlab-mcp-server", - version: "0.0.1", + version: "1.0.7-fix", }, { capabilities: { tools: {}, @@ -345,7 +345,11 @@ async function updateMergeRequest(projectId, mergeRequestIid, options) { async function createNote(projectId, noteableType, // 'issue' 또는 'merge_request' 타입 명시 noteableIid, body) { // ⚙️ 응답 타입은 GitLab API 문서에 따라 조정 가능 - const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/${noteableType}/${noteableIid}/notes`); + const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/${noteableType}s/${noteableIid}/notes` // Using plural form (issues/merge_requests) as per GitLab API documentation + ); + console.log(`DEBUG - createNote - URL: ${url.toString()}`); + console.log(`DEBUG - createNote - projectId: ${projectId}, noteableType: ${noteableType}, noteableIid: ${noteableIid}`); + console.log(`DEBUG - createNote - GITLAB_API_URL: ${GITLAB_API_URL}`); const response = await fetch(url.toString(), { method: "POST", headers: DEFAULT_HEADERS, diff --git a/index.ts b/index.ts index d499d61..34c075f 100644 --- a/index.ts +++ b/index.ts @@ -588,6 +588,10 @@ async function createNote( )}/${noteableType}s/${noteableIid}/notes` // Using plural form (issues/merge_requests) as per GitLab API documentation ); + console.log(`DEBUG - createNote - URL: ${url.toString()}`); + console.log(`DEBUG - createNote - projectId: ${projectId}, noteableType: ${noteableType}, noteableIid: ${noteableIid}`); + console.log(`DEBUG - createNote - GITLAB_API_URL: ${GITLAB_API_URL}`); + const response = await fetch(url.toString(), { method: "POST", headers: DEFAULT_HEADERS, From 0fd73ec04e8fb9a5d7fa855885274733a52c2994 Mon Sep 17 00:00:00 2001 From: Admin Date: Mon, 17 Mar 2025 14:18:37 -0700 Subject: [PATCH 5/8] Add debug info to create_note handler response --- build/index.js | 20 +++++++++++++++++--- index.ts | 27 ++++++++++++++++++++++----- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/build/index.js b/build/index.js index ef785bf..817219f 100755 --- a/build/index.js +++ b/build/index.js @@ -541,10 +541,24 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { try { const args = CreateNoteSchema.parse(request.params.arguments); const { project_id, noteable_type, noteable_iid, body } = args; - const note = await createNote(project_id, noteable_type, noteable_iid, body); - return { - content: [{ type: "text", text: JSON.stringify(note, null, 2) }], + // Debug info that will be included in the response + const debugInfo = { + gitlab_api_url: GITLAB_API_URL, + project_id, + noteable_type, + noteable_iid, + constructed_url: `${GITLAB_API_URL}/projects/${encodeURIComponent(project_id)}/${noteable_type}s/${noteable_iid}/notes` }; + try { + const note = await createNote(project_id, noteable_type, noteable_iid, body); + return { + content: [{ type: "text", text: JSON.stringify(note, null, 2) }], + }; + } + catch (error) { + // Include debug info in the error message + throw new Error(`Error with debug info: ${JSON.stringify(debugInfo)}\n${error instanceof Error ? error.message : String(error)}`); + } } catch (error) { if (error instanceof z.ZodError) { diff --git a/index.ts b/index.ts index 34c075f..37ffeb1 100644 --- a/index.ts +++ b/index.ts @@ -835,15 +835,32 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { try { const args = CreateNoteSchema.parse(request.params.arguments); const { project_id, noteable_type, noteable_iid, body } = args; - const note = await createNote( + + // Debug info that will be included in the response + const debugInfo = { + gitlab_api_url: GITLAB_API_URL, project_id, noteable_type, noteable_iid, - body - ); - return { - content: [{ type: "text", text: JSON.stringify(note, null, 2) }], + constructed_url: `${GITLAB_API_URL}/projects/${encodeURIComponent(project_id)}/${noteable_type}s/${noteable_iid}/notes` }; + + try { + const note = await createNote( + project_id, + noteable_type, + noteable_iid, + body + ); + return { + content: [{ type: "text", text: JSON.stringify(note, null, 2) }], + }; + } catch (error) { + // Include debug info in the error message + throw new Error( + `Error with debug info: ${JSON.stringify(debugInfo)}\n${error instanceof Error ? error.message : String(error)}` + ); + } } catch (error) { if (error instanceof z.ZodError) { throw new Error( From 4740a4b4f74aaf794044c710748b09e0d3e935ae Mon Sep 17 00:00:00 2001 From: Admin Date: Mon, 17 Mar 2025 14:22:37 -0700 Subject: [PATCH 6/8] Fix createNote function to not duplicate /api/v4 in URL --- build/index.js | 3 +++ index.ts | 3 +++ 2 files changed, 6 insertions(+) diff --git a/build/index.js b/build/index.js index 817219f..42b619a 100755 --- a/build/index.js +++ b/build/index.js @@ -345,8 +345,11 @@ async function updateMergeRequest(projectId, mergeRequestIid, options) { async function createNote(projectId, noteableType, // 'issue' 또는 'merge_request' 타입 명시 noteableIid, body) { // ⚙️ 응답 타입은 GitLab API 문서에 따라 조정 가능 + // Don't add /api/v4 again since it's already included in GITLAB_API_URL const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/${noteableType}s/${noteableIid}/notes` // Using plural form (issues/merge_requests) as per GitLab API documentation ); + // Add some debug logging + console.log(`DEBUG - createNote function called`); console.log(`DEBUG - createNote - URL: ${url.toString()}`); console.log(`DEBUG - createNote - projectId: ${projectId}, noteableType: ${noteableType}, noteableIid: ${noteableIid}`); console.log(`DEBUG - createNote - GITLAB_API_URL: ${GITLAB_API_URL}`); diff --git a/index.ts b/index.ts index 37ffeb1..1f22471 100644 --- a/index.ts +++ b/index.ts @@ -582,12 +582,15 @@ async function createNote( body: string ): Promise { // ⚙️ 응답 타입은 GitLab API 문서에 따라 조정 가능 + // Don't add /api/v4 again since it's already included in GITLAB_API_URL const url = new URL( `${GITLAB_API_URL}/projects/${encodeURIComponent( projectId )}/${noteableType}s/${noteableIid}/notes` // Using plural form (issues/merge_requests) as per GitLab API documentation ); + // Add some debug logging + console.log(`DEBUG - createNote function called`); console.log(`DEBUG - createNote - URL: ${url.toString()}`); console.log(`DEBUG - createNote - projectId: ${projectId}, noteableType: ${noteableType}, noteableIid: ${noteableIid}`); console.log(`DEBUG - createNote - GITLAB_API_URL: ${GITLAB_API_URL}`); From 696a6dd1021b85b5c915c223f6d2da91c575c3e8 Mon Sep 17 00:00:00 2001 From: Admin Date: Mon, 17 Mar 2025 14:26:26 -0700 Subject: [PATCH 7/8] Add enhanced debugging to createNote function --- build/index.js | 37 ++++++++++++++++++++++++++----------- index.ts | 41 ++++++++++++++++++++++++++++++----------- 2 files changed, 56 insertions(+), 22 deletions(-) diff --git a/build/index.js b/build/index.js index 42b619a..beb11cc 100755 --- a/build/index.js +++ b/build/index.js @@ -346,20 +346,32 @@ async function createNote(projectId, noteableType, // 'issue' 또는 'merge_requ noteableIid, body) { // ⚙️ 응답 타입은 GitLab API 문서에 따라 조정 가능 // Don't add /api/v4 again since it's already included in GITLAB_API_URL + console.error("DEBUG - createNote - GITLAB_API_URL from env: " + process.env.GITLAB_API_URL); + console.error("DEBUG - createNote - GITLAB_API_URL in code: " + GITLAB_API_URL); const url = new URL(`${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/${noteableType}s/${noteableIid}/notes` // Using plural form (issues/merge_requests) as per GitLab API documentation ); // Add some debug logging - console.log(`DEBUG - createNote function called`); - console.log(`DEBUG - createNote - URL: ${url.toString()}`); - console.log(`DEBUG - createNote - projectId: ${projectId}, noteableType: ${noteableType}, noteableIid: ${noteableIid}`); - console.log(`DEBUG - createNote - GITLAB_API_URL: ${GITLAB_API_URL}`); - const response = await fetch(url.toString(), { - method: "POST", - headers: DEFAULT_HEADERS, - body: JSON.stringify({ body }), - }); - await handleGitLabError(response); - return await response.json(); // ⚙️ 응답 타입은 GitLab API 문서에 따라 조정 가능, 필요하면 스키마 정의 + console.error("DEBUG - createNote function called"); + console.error(`DEBUG - createNote - URL: ${url.toString()}`); + console.error(`DEBUG - createNote - projectId: ${projectId}, noteableType: ${noteableType}, noteableIid: ${noteableIid}`); + try { + const response = await fetch(url.toString(), { + method: "POST", + headers: DEFAULT_HEADERS, + body: JSON.stringify({ body }), + }); + if (!response.ok) { + const errorText = await response.text(); + console.error(`DEBUG - createNote - Error response: ${response.status} ${response.statusText}`); + console.error(`DEBUG - createNote - Error body: ${errorText}`); + throw new Error(`GitLab API error: ${response.status} ${response.statusText}\n${errorText}`); + } + return await response.json(); + } + catch (error) { + console.error(`DEBUG - createNote - Exception: ${error instanceof Error ? error.message : String(error)}`); + throw error; + } } server.setRequestHandler(ListToolsRequestSchema, async () => { return { @@ -586,6 +598,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { } }); async function runServer() { + console.error("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + console.error("!!! RUNNING VERSION 1.0.7-fix WITH URL DEBUGGING FIX !!!"); + console.error("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); const transport = new StdioServerTransport(); await server.connect(transport); console.error("GitLab MCP Server running on stdio"); diff --git a/index.ts b/index.ts index 1f22471..ff11901 100644 --- a/index.ts +++ b/index.ts @@ -583,6 +583,9 @@ async function createNote( ): Promise { // ⚙️ 응답 타입은 GitLab API 문서에 따라 조정 가능 // Don't add /api/v4 again since it's already included in GITLAB_API_URL + console.error("DEBUG - createNote - GITLAB_API_URL from env: " + process.env.GITLAB_API_URL); + console.error("DEBUG - createNote - GITLAB_API_URL in code: " + GITLAB_API_URL); + const url = new URL( `${GITLAB_API_URL}/projects/${encodeURIComponent( projectId @@ -590,19 +593,31 @@ async function createNote( ); // Add some debug logging - console.log(`DEBUG - createNote function called`); - console.log(`DEBUG - createNote - URL: ${url.toString()}`); - console.log(`DEBUG - createNote - projectId: ${projectId}, noteableType: ${noteableType}, noteableIid: ${noteableIid}`); - console.log(`DEBUG - createNote - GITLAB_API_URL: ${GITLAB_API_URL}`); + console.error("DEBUG - createNote function called"); + console.error(`DEBUG - createNote - URL: ${url.toString()}`); + console.error(`DEBUG - createNote - projectId: ${projectId}, noteableType: ${noteableType}, noteableIid: ${noteableIid}`); - const response = await fetch(url.toString(), { - method: "POST", - headers: DEFAULT_HEADERS, - body: JSON.stringify({ body }), - }); + try { + const response = await fetch(url.toString(), { + method: "POST", + headers: DEFAULT_HEADERS, + body: JSON.stringify({ body }), + }); - await handleGitLabError(response); - return await response.json(); // ⚙️ 응답 타입은 GitLab API 문서에 따라 조정 가능, 필요하면 스키마 정의 + if (!response.ok) { + const errorText = await response.text(); + console.error(`DEBUG - createNote - Error response: ${response.status} ${response.statusText}`); + console.error(`DEBUG - createNote - Error body: ${errorText}`); + throw new Error( + `GitLab API error: ${response.status} ${response.statusText}\n${errorText}` + ); + } + + return await response.json(); + } catch (error) { + console.error(`DEBUG - createNote - Exception: ${error instanceof Error ? error.message : String(error)}`); + throw error; + } } server.setRequestHandler(ListToolsRequestSchema, async () => { @@ -892,6 +907,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { }); async function runServer() { + console.error("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + console.error("!!! RUNNING VERSION 1.0.7-fix WITH URL DEBUGGING FIX !!!"); + console.error("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + const transport = new StdioServerTransport(); await server.connect(transport); console.error("GitLab MCP Server running on stdio"); From 6a61a510b92ab24bc6647febc9ec73264be4b0fe Mon Sep 17 00:00:00 2001 From: Admin Date: Mon, 17 Mar 2025 17:34:21 -0700 Subject: [PATCH 8/8] Fix URL construction and add smart API URL normalization --- index.ts | 183 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 94 insertions(+), 89 deletions(-) diff --git a/index.ts b/index.ts index ff11901..b1c70fa 100644 --- a/index.ts +++ b/index.ts @@ -11,6 +11,8 @@ import { z } from "zod"; import { zodToJsonSchema } from "zod-to-json-schema"; import { fileURLToPath } from "url"; import { dirname, resolve } from "path"; +import fs from "fs"; +import path from "path"; import { GitLabForkSchema, GitLabReferenceSchema, @@ -54,10 +56,24 @@ import { CreateNoteSchema, } from "./schemas.js"; +// Read version from package.json +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); +const packageJsonPath = path.resolve(__dirname, '../package.json'); +let SERVER_VERSION = "unknown"; +try { + if (fs.existsSync(packageJsonPath)) { + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + SERVER_VERSION = packageJson.version || SERVER_VERSION; + } +} catch (error) { + console.error("Warning: Could not read version from package.json:", error); +} + const server = new Server( { name: "better-gitlab-mcp-server", - version: "1.0.7-fix", + version: SERVER_VERSION, }, { capabilities: { @@ -67,8 +83,34 @@ const server = new Server( ); const GITLAB_PERSONAL_ACCESS_TOKEN = process.env.GITLAB_PERSONAL_ACCESS_TOKEN; -const GITLAB_API_URL = - process.env.GITLAB_API_URL || "https://gitlab.com/api/v4"; + +// Smart URL handling for GitLab API +function normalizeGitLabApiUrl(url?: string): string { + if (!url) { + return "https://gitlab.com/api/v4"; + } + + // Remove trailing slash if present + let normalizedUrl = url.endsWith('/') ? url.slice(0, -1) : url; + + // Check if URL already has /api/v4 + if (!normalizedUrl.endsWith('/api/v4') && !normalizedUrl.endsWith('/api/v4/')) { + // Append /api/v4 if not already present + normalizedUrl = `${normalizedUrl}/api/v4`; + } + + return normalizedUrl; +} + +// Use the normalizeGitLabApiUrl function to handle various URL formats +const GITLAB_API_URL = normalizeGitLabApiUrl(process.env.GITLAB_API_URL || ""); + +// Add debug logging for API URL construction +console.log("=== MCP Server Configuration ==="); +console.log(`GITLAB_API_URL = "${GITLAB_API_URL}"`); +console.log(`Example project API URL = "${GITLAB_API_URL}/projects/123"`); +console.log(`Example Notes API URL = "${GITLAB_API_URL}/projects/123/issues/1/notes"`); +console.log("==============================="); if (!GITLAB_PERSONAL_ACCESS_TOKEN) { console.error("GITLAB_PERSONAL_ACCESS_TOKEN environment variable is not set"); @@ -101,7 +143,7 @@ async function forkProject( ): Promise { // API 엔드포인트 URL 생성 const url = new URL( - `${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent(projectId)}/fork` + `${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/fork` ); if (namespace) { @@ -129,7 +171,7 @@ async function createBranch( options: z.infer ): Promise { const url = new URL( - `${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent( + `${GITLAB_API_URL}/projects/${encodeURIComponent( projectId )}/repository/branches` ); @@ -150,7 +192,7 @@ async function createBranch( // 프로젝트의 기본 브랜치 조회 async function getDefaultBranchRef(projectId: string): Promise { const url = new URL( - `${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent(projectId)}` + `${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}` ); const response = await fetch(url.toString(), { @@ -176,7 +218,7 @@ async function getFileContents( } const url = new URL( - `${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent( + `${GITLAB_API_URL}/projects/${encodeURIComponent( projectId )}/repository/files/${encodedPath}` ); @@ -213,7 +255,7 @@ async function createIssue( options: z.infer ): Promise { const url = new URL( - `${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent(projectId)}/issues` + `${GITLAB_API_URL}/projects/${encodeURIComponent(projectId)}/issues` ); const response = await fetch(url.toString(), { @@ -244,7 +286,7 @@ async function createMergeRequest( options: z.infer ): Promise { const url = new URL( - `${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent( + `${GITLAB_API_URL}/projects/${encodeURIComponent( projectId )}/merge_requests` ); @@ -292,7 +334,7 @@ async function createOrUpdateFile( ): Promise { const encodedPath = encodeURIComponent(filePath); const url = new URL( - `${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent( + `${GITLAB_API_URL}/projects/${encodeURIComponent( projectId )}/repository/files/${encodedPath}` ); @@ -344,7 +386,7 @@ async function createTree( ref?: string ): Promise { const url = new URL( - `${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent( + `${GITLAB_API_URL}/projects/${encodeURIComponent( projectId )}/repository/tree` ); @@ -392,7 +434,7 @@ async function createCommit( actions: FileOperation[] ): Promise { const url = new URL( - `${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent( + `${GITLAB_API_URL}/projects/${encodeURIComponent( projectId )}/repository/commits` ); @@ -437,7 +479,7 @@ async function searchProjects( page: number = 1, perPage: number = 20 ): Promise { - const url = new URL(`${GITLAB_API_URL}/api/v4/projects`); + const url = new URL(`${GITLAB_API_URL}/projects`); url.searchParams.append("search", query); url.searchParams.append("page", page.toString()); url.searchParams.append("per_page", perPage.toString()); @@ -477,7 +519,7 @@ async function searchProjects( async function createRepository( options: z.infer ): Promise { - const response = await fetch(`${GITLAB_API_URL}/api/v4/projects`, { + const response = await fetch(`${GITLAB_API_URL}/projects`, { method: "POST", headers: { Accept: "application/json", @@ -511,7 +553,7 @@ async function getMergeRequest( mergeRequestIid: number ): Promise { const url = new URL( - `${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent( + `${GITLAB_API_URL}/projects/${encodeURIComponent( projectId )}/merge_requests/${mergeRequestIid}` ); @@ -531,7 +573,7 @@ async function getMergeRequestDiffs( view?: "inline" | "parallel" ): Promise { const url = new URL( - `${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent( + `${GITLAB_API_URL}/projects/${encodeURIComponent( projectId )}/merge_requests/${mergeRequestIid}/changes` ); @@ -559,7 +601,7 @@ async function updateMergeRequest( > ): Promise { const url = new URL( - `${GITLAB_API_URL}/api/v4/projects/${encodeURIComponent( + `${GITLAB_API_URL}/projects/${encodeURIComponent( projectId )}/merge_requests/${mergeRequestIid}` ); @@ -582,42 +624,26 @@ async function createNote( body: string ): Promise { // ⚙️ 응답 타입은 GitLab API 문서에 따라 조정 가능 - // Don't add /api/v4 again since it's already included in GITLAB_API_URL - console.error("DEBUG - createNote - GITLAB_API_URL from env: " + process.env.GITLAB_API_URL); - console.error("DEBUG - createNote - GITLAB_API_URL in code: " + GITLAB_API_URL); - const url = new URL( `${GITLAB_API_URL}/projects/${encodeURIComponent( projectId )}/${noteableType}s/${noteableIid}/notes` // Using plural form (issues/merge_requests) as per GitLab API documentation ); - // Add some debug logging - console.error("DEBUG - createNote function called"); - console.error(`DEBUG - createNote - URL: ${url.toString()}`); - console.error(`DEBUG - createNote - projectId: ${projectId}, noteableType: ${noteableType}, noteableIid: ${noteableIid}`); + const response = await fetch(url.toString(), { + method: "POST", + headers: DEFAULT_HEADERS, + body: JSON.stringify({ body }), + }); - try { - const response = await fetch(url.toString(), { - method: "POST", - headers: DEFAULT_HEADERS, - body: JSON.stringify({ body }), - }); - - if (!response.ok) { - const errorText = await response.text(); - console.error(`DEBUG - createNote - Error response: ${response.status} ${response.statusText}`); - console.error(`DEBUG - createNote - Error body: ${errorText}`); - throw new Error( - `GitLab API error: ${response.status} ${response.statusText}\n${errorText}` - ); - } - - return await response.json(); - } catch (error) { - console.error(`DEBUG - createNote - Exception: ${error instanceof Error ? error.message : String(error)}`); - throw error; + if (!response.ok) { + const errorText = await response.text(); + throw new Error( + `GitLab API error: ${response.status} ${response.statusText}\n${errorText}` + ); } + + return await response.json(); } server.setRequestHandler(ListToolsRequestSchema, async () => { @@ -850,45 +876,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { } case "create_note": { - try { - const args = CreateNoteSchema.parse(request.params.arguments); - const { project_id, noteable_type, noteable_iid, body } = args; + const args = CreateNoteSchema.parse(request.params.arguments); + const { project_id, noteable_type, noteable_iid, body } = args; - // Debug info that will be included in the response - const debugInfo = { - gitlab_api_url: GITLAB_API_URL, - project_id, - noteable_type, - noteable_iid, - constructed_url: `${GITLAB_API_URL}/projects/${encodeURIComponent(project_id)}/${noteable_type}s/${noteable_iid}/notes` - }; - - try { - const note = await createNote( - project_id, - noteable_type, - noteable_iid, - body - ); - return { - content: [{ type: "text", text: JSON.stringify(note, null, 2) }], - }; - } catch (error) { - // Include debug info in the error message - throw new Error( - `Error with debug info: ${JSON.stringify(debugInfo)}\n${error instanceof Error ? error.message : String(error)}` - ); - } - } catch (error) { - if (error instanceof z.ZodError) { - throw new Error( - `Invalid arguments: ${error.errors - .map((e) => `${e.path.join(".")}: ${e.message}`) - .join(", ")}` - ); - } - throw error; - } + const note = await createNote( + project_id, + noteable_type, + noteable_iid, + body + ); + return { + content: [{ type: "text", text: JSON.stringify(note, null, 2) }], + }; } default: @@ -907,13 +906,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { }); async function runServer() { - console.error("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); - console.error("!!! RUNNING VERSION 1.0.7-fix WITH URL DEBUGGING FIX !!!"); - console.error("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + try { + console.error("========================"); + console.error(`GitLab MCP Server v${SERVER_VERSION}`); + console.error(`API URL: ${GITLAB_API_URL}`); + console.error("========================"); - const transport = new StdioServerTransport(); - await server.connect(transport); - console.error("GitLab MCP Server running on stdio"); + const transport = new StdioServerTransport(); + await server.connect(transport); + console.error("GitLab MCP Server running on stdio"); + } catch (error) { + console.error("Error initializing server:", error); + process.exit(1); + } } runServer().catch((error) => {