From 111a786013b0a153049bd49b10b48a8fa9b00141 Mon Sep 17 00:00:00 2001 From: Tim Pearson Date: Tue, 12 May 2026 23:24:36 -0400 Subject: [PATCH] fix: write tools honor GITLAB_TOKEN fallback (1.15.1) Write tool handlers had an early-exit guard that required per-call userCredentials, contradicting the documented design that GITLAB_TOKEN is a full-access fallback usable for both reads and writes. The guard fired before getClient() could resolve the env token, so stdio / no-Authorization-header callers saw "User authentication is required..." even with GITLAB_TOKEN configured. Reads were unaffected because read handlers don't have the guard. Removes the guard from create_issue, create_merge_request, update_issue, delete_issue, update_merge_request, manage_pipeline, create_note, delete_note, update_note, create_broadcast_message, update_broadcast_message, delete_broadcast_message, and the requiresWrite branch of execute_custom_query. getClient() already throws an actionable error if no token at all is configured. Fixes #32. --- .claude-plugin/plugin.json | 2 +- CHANGELOG.md | 17 +++++++++++++++++ package-lock.json | 4 ++-- package.json | 2 +- src/tools.ts | 39 -------------------------------------- 5 files changed, 21 insertions(+), 43 deletions(-) diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index c18953b..01f2f39 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "gitlab-mcp", "description": "GitLab MCP server with GraphQL discovery and team activity tools", - "version": "1.15.0", + "version": "1.15.1", "icon": "assets/logo.svg", "author": { "name": "Tim Pearson" diff --git a/CHANGELOG.md b/CHANGELOG.md index ba788bd..b5b84c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.15.1] - 2026-05-07 + +### Fixed + +- Write tools (`create_merge_request`, `create_issue`, `update_issue`, + `delete_issue`, `update_merge_request`, `manage_pipeline`, `create_note`, + `delete_note`, `update_note`, `create_broadcast_message`, + `update_broadcast_message`, `delete_broadcast_message`, and the + `requiresWrite` branch of `execute_custom_query`) no longer reject the + request when no per-call `userCredentials` are supplied. They now fall + back to `GITLAB_TOKEN` exactly as documented — a handler-level guard + was short-circuiting the four-step token resolution in `getClient()`, + so writes failed with "User authentication is required…" even when a + full-access env token was configured. Reads were unaffected. Per-call + user credentials and HTTP `Authorization: Bearer` flows continue to + work unchanged. Fixes #32. + ## [1.15.0] - 2026-04-27 ### Added diff --git a/package-lock.json b/package-lock.json index ced16e2..1fc5437 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@ttpears/gitlab-mcp-server", - "version": "1.15.0", + "version": "1.15.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@ttpears/gitlab-mcp-server", - "version": "1.15.0", + "version": "1.15.1", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^1.26.0", diff --git a/package.json b/package.json index 725b6ae..aa44246 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ttpears/gitlab-mcp-server", - "version": "1.15.0", + "version": "1.15.1", "description": "GitLab MCP Server with GraphQL discovery", "main": "dist/index.js", "module": "./src/index.ts", diff --git a/src/tools.ts b/src/tools.ts index 1473a97..bf397bc 100644 --- a/src/tools.ts +++ b/src/tools.ts @@ -209,9 +209,6 @@ const createIssueTool: Tool = { })), handler: async (input, client, userConfig) => { const credentials = input.userCredentials ? validateUserConfig(input.userCredentials) : userConfig; - if (!credentials) { - throw new Error('User authentication is required for creating issues. Please provide your GitLab credentials.'); - } const result = await client.createIssue(input.projectPath, input.title, input.description, credentials); const payload = result.createIssue; if (payload.errors && payload.errors.length > 0) { @@ -241,9 +238,6 @@ const createMergeRequestTool: Tool = { })), handler: async (input, client, userConfig) => { const credentials = input.userCredentials ? validateUserConfig(input.userCredentials) : userConfig; - if (!credentials) { - throw new Error('User authentication is required for creating merge requests. Please provide your GitLab credentials.'); - } const result = await client.createMergeRequest( input.projectPath, input.title, @@ -279,9 +273,6 @@ const executeCustomQueryTool: Tool = { })), handler: async (input, client, userConfig) => { const credentials = input.userCredentials ? validateUserConfig(input.userCredentials) : userConfig; - if (input.requiresWrite && !credentials) { - throw new Error('User authentication is required for write operations. Please provide your GitLab credentials.'); - } return await client.query(input.query, input.variables, credentials, input.requiresWrite); }, }; @@ -330,9 +321,6 @@ const updateIssueTool: Tool = { })), handler: async (input, client, userConfig) => { const credentials = input.userCredentials ? validateUserConfig(input.userCredentials) : userConfig; - if (!credentials) { - throw new Error('User authentication is required to update issues.'); - } const result = await client.updateIssueComposite( input.projectPath, input.iid, @@ -363,9 +351,6 @@ const deleteIssueTool: Tool = { })), handler: async (input, client, userConfig) => { const credentials = input.userCredentials ? validateUserConfig(input.userCredentials) : userConfig; - if (!credentials) { - throw new Error('User authentication is required for deleting issues.'); - } await client.destroyIssue(input.projectPath.trim(), input.iid.trim(), credentials); return { projectPath: input.projectPath, iid: input.iid, deleted: true }; }, @@ -393,9 +378,6 @@ const updateMergeRequestTool: Tool = { })), handler: async (input, client, userConfig) => { const credentials = input.userCredentials ? validateUserConfig(input.userCredentials) : userConfig; - if (!credentials) { - throw new Error('User authentication is required to update merge requests.'); - } const result = await client.updateMergeRequestComposite( input.projectPath, input.iid, @@ -913,9 +895,6 @@ const managePipelineTool: Tool = { })), handler: async (input, client, userConfig) => { const credentials = input.userCredentials ? validateUserConfig(input.userCredentials) : userConfig; - if (!credentials) { - throw new Error('User authentication is required for pipeline management. Please provide your GitLab credentials.'); - } return await client.managePipeline(input.projectPath, input.pipelineIid, input.action, credentials); }, }; @@ -1216,9 +1195,6 @@ const createNoteTool: Tool = { })), handler: async (input, client, userConfig) => { const credentials = input.userCredentials ? validateUserConfig(input.userCredentials) : userConfig; - if (!credentials) { - throw new Error('User authentication is required for creating notes. Please provide your GitLab credentials.'); - } const result = await client.createNote(input.projectPath, input.noteableType, input.iid, input.body, input.internal, credentials); if (result.errors && result.errors.length > 0) { throw new Error(`Failed to create note: ${result.errors.join(', ')}`); @@ -1240,9 +1216,6 @@ const deleteNoteTool: Tool = { })), handler: async (input, client, userConfig) => { const credentials = input.userCredentials ? validateUserConfig(input.userCredentials) : userConfig; - if (!credentials) { - throw new Error('User authentication is required for deleting notes.'); - } await client.destroyNote(input.noteId.trim(), credentials); return { noteId: input.noteId, deleted: true }; }, @@ -1262,9 +1235,6 @@ const updateNoteTool: Tool = { })), handler: async (input, client, userConfig) => { const credentials = input.userCredentials ? validateUserConfig(input.userCredentials) : userConfig; - if (!credentials) { - throw new Error('User authentication is required for updating notes.'); - } const result = await client.updateNote(input.noteId.trim(), input.body, credentials); return result.note; }, @@ -1704,9 +1674,6 @@ const createBroadcastMessageTool: Tool = { inputSchema: withUserAuth(z.object(BroadcastMessageFields)), handler: async (input, client, userConfig) => { const credentials = input.userCredentials ? validateUserConfig(input.userCredentials) : userConfig; - if (!credentials) { - throw new Error('User authentication is required for creating broadcast messages.'); - } const { userCredentials, ...body } = input; return client.createBroadcastMessage(body, credentials); }, @@ -1734,9 +1701,6 @@ const updateBroadcastMessageTool: Tool = { })), handler: async (input, client, userConfig) => { const credentials = input.userCredentials ? validateUserConfig(input.userCredentials) : userConfig; - if (!credentials) { - throw new Error('User authentication is required for updating broadcast messages.'); - } const { id, userCredentials, ...body } = input; return client.updateBroadcastMessage(id, body, credentials); }, @@ -1754,9 +1718,6 @@ const deleteBroadcastMessageTool: Tool = { })), handler: async (input, client, userConfig) => { const credentials = input.userCredentials ? validateUserConfig(input.userCredentials) : userConfig; - if (!credentials) { - throw new Error('User authentication is required for deleting broadcast messages.'); - } await client.deleteBroadcastMessage(input.id, credentials); return { id: input.id, deleted: true }; },