Compare commits

...

8 Commits

Author SHA1 Message Date
1350a024b5 Merge pull request #44 from huerlisi/feat/add-issue-notes-support
feat: add issue discussions support
2025-05-21 03:36:33 +09:00
4c57c37888 feat: add issue discussions support
Added `list_issue_discussions` tool to support GitLab issue discussions
similar to merge request discussions.
2025-05-20 15:45:23 +02:00
e4a28a9a47 버전 1.0.39로 업데이트 2025-05-20 18:34:05 +09:00
9f1e7b5bca Merge pull request #42 from vlucaswang/feat/add-docker
feat: add docker image and push to dockerhub
2025-05-20 18:32:19 +09:00
f37e210ee8 Merge pull request #41 from kamibayashi/fixed_resolve_outdated_diff_discussions_nullable
fixed resolve_outdated_diff_discussions nullable
2025-05-20 18:28:08 +09:00
6f789692be feat: add docker image and push to dockerhub 2025-05-20 16:04:37 +09:30
1bb70dccb9 fixed resolve_outdated_diff_discussions nullable 2025-05-19 17:18:01 +09:00
676bbcd4dd docs: add release-notes.md 2025-05-17 15:41:14 +09:00
7 changed files with 172 additions and 30 deletions

24
Dockerfile Normal file
View File

@ -0,0 +1,24 @@
FROM node:22.15-alpine AS builder
COPY . /app
COPY tsconfig.json /tsconfig.json
WORKDIR /app
RUN --mount=type=cache,target=/root/.npm npm install
RUN --mount=type=cache,target=/root/.npm-production npm ci --ignore-scripts --omit-dev
FROM node:22.12-alpine AS release
WORKDIR /app
COPY --from=builder /app/build /app/build
COPY --from=builder /app/package.json /app/package.json
COPY --from=builder /app/package-lock.json /app/package-lock.json
ENV NODE_ENV=production
RUN npm ci --ignore-scripts --omit-dev
ENTRYPOINT ["node", "build/index.js"]

View File

@ -14,6 +14,8 @@ GitLab MCP(Model Context Protocol) Server. **Includes bug fixes and improvements
When using with the Claude App, you need to set up your API key and URLs directly. When using with the Claude App, you need to set up your API key and URLs directly.
#### npx
```json ```json
{ {
"mcpServers": { "mcpServers": {
@ -31,8 +33,39 @@ When using with the Claude App, you need to set up your API key and URLs directl
} }
``` ```
### Environment Variables #### Docker
```json
{
"mcpServers": {
"GitLab communication server": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-e",
"GITLAB_PERSONAL_ACCESS_TOKEN",
"-e",
"GITLAB_API_URL",
"-e",
"GITLAB_READ_ONLY_MODE",
"-e",
"USE_GITLAB_WIKI",
"nkwd/mcp-gitlab"
],
"env": {
"GITLAB_PERSONAL_ACCESS": "your_gitlab_token",
"GITLAB_API_URL": "https://gitlab.com/api/v4", // Optional, for self-hosted GitLab
"GITLAB_READ_ONLY_MODE": "false",
"USE_GITLAB_WIKI": "true"
}
}
}
}
```
### Environment Variables
- `GITLAB_PERSONAL_ACCESS_TOKEN`: Your GitLab personal access token. - `GITLAB_PERSONAL_ACCESS_TOKEN`: Your GitLab personal access token.
- `GITLAB_API_URL`: Your GitLab API URL. (Default: `https://gitlab.com/api/v4`) - `GITLAB_API_URL`: Your GitLab API URL. (Default: `https://gitlab.com/api/v4`)
@ -42,6 +75,7 @@ When using with the Claude App, you need to set up your API key and URLs directl
## Tools 🛠️ ## Tools 🛠️
+<!-- TOOLS-START --> +<!-- TOOLS-START -->
1. `create_or_update_file` - Create or update a single file in a GitLab project 1. `create_or_update_file` - Create or update a single file in a GitLab project
2. `search_repositories` - Search for GitLab projects 2. `search_repositories` - Search for GitLab projects
3. `create_repository` - Create a new GitLab project 3. `create_repository` - Create a new GitLab project
@ -64,24 +98,25 @@ When using with the Claude App, you need to set up your API key and URLs directl
20. `update_issue` - Update an issue in a GitLab project 20. `update_issue` - Update an issue in a GitLab project
21. `delete_issue` - Delete an issue from a GitLab project 21. `delete_issue` - Delete an issue from a GitLab project
22. `list_issue_links` - List all issue links for a specific issue 22. `list_issue_links` - List all issue links for a specific issue
23. `get_issue_link` - Get a specific issue link 23. `list_issue_discussions` - List discussions for an issue in a GitLab project
24. `create_issue_link` - Create an issue link between two issues 24. `get_issue_link` - Get a specific issue link
25. `delete_issue_link` - Delete an issue link 25. `create_issue_link` - Create an issue link between two issues
26. `list_namespaces` - List all namespaces available to the current user 26. `delete_issue_link` - Delete an issue link
27. `get_namespace` - Get details of a namespace by ID or path 27. `list_namespaces` - List all namespaces available to the current user
28. `verify_namespace` - Verify if a namespace path exists 28. `get_namespace` - Get details of a namespace by ID or path
29. `get_project` - Get details of a specific project 29. `verify_namespace` - Verify if a namespace path exists
30. `list_projects` - List projects accessible by the current user 30. `get_project` - Get details of a specific project
31. `list_labels` - List labels for a project 31. `list_projects` - List projects accessible by the current user
32. `get_label` - Get a single label from a project 32. `list_labels` - List labels for a project
33. `create_label` - Create a new label in a project 33. `get_label` - Get a single label from a project
34. `update_label` - Update an existing label in a project 34. `create_label` - Create a new label in a project
35. `delete_label` - Delete a label from a project 35. `update_label` - Update an existing label in a project
36. `list_group_projects` - List projects in a GitLab group with filtering options 36. `delete_label` - Delete a label from a project
37. `list_wiki_pages` - List wiki pages in a GitLab project 37. `list_group_projects` - List projects in a GitLab group with filtering options
38. `get_wiki_page` - Get details of a specific wiki page 38. `list_wiki_pages` - List wiki pages in a GitLab project
39. `create_wiki_page` - Create a new wiki page in a GitLab project 39. `get_wiki_page` - Get details of a specific wiki page
40. `update_wiki_page` - Update an existing wiki page in a GitLab project 40. `create_wiki_page` - Create a new wiki page in a GitLab project
41. `delete_wiki_page` - Delete a wiki page from a GitLab project 41. `update_wiki_page` - Update an existing wiki page in a GitLab project
42. `get_repository_tree` - Get the repository tree for a GitLab project (list files and directories) 42. `delete_wiki_page` - Delete a wiki page from a GitLab project
43. `get_repository_tree` - Get the repository tree for a GitLab project (list files and directories)
<!-- TOOLS-END --> <!-- TOOLS-END -->

View File

@ -61,6 +61,7 @@ import {
GitLabIssueLinkSchema, GitLabIssueLinkSchema,
GitLabIssueWithLinkDetailsSchema, GitLabIssueWithLinkDetailsSchema,
ListIssueLinksSchema, ListIssueLinksSchema,
ListIssueDiscussionsSchema,
GetIssueLinkSchema, GetIssueLinkSchema,
CreateIssueLinkSchema, CreateIssueLinkSchema,
DeleteIssueLinkSchema, DeleteIssueLinkSchema,
@ -311,6 +312,11 @@ const allTools = [
description: "List all issue links for a specific issue", description: "List all issue links for a specific issue",
inputSchema: zodToJsonSchema(ListIssueLinksSchema), inputSchema: zodToJsonSchema(ListIssueLinksSchema),
}, },
{
name: "list_issue_discussions",
description: "List discussions for an issue in a GitLab project",
inputSchema: zodToJsonSchema(ListIssueDiscussionsSchema),
},
{ {
name: "get_issue_link", name: "get_issue_link",
description: "Get a specific issue link", description: "Get a specific issue link",
@ -424,6 +430,7 @@ const readOnlyTools = [
"list_issues", "list_issues",
"get_issue", "get_issue",
"list_issue_links", "list_issue_links",
"list_issue_discussions",
"get_issue_link", "get_issue_link",
"list_namespaces", "list_namespaces",
"get_namespace", "get_namespace",
@ -1023,6 +1030,56 @@ async function listMergeRequestDiscussions(
return z.array(GitLabDiscussionSchema).parse(data); return z.array(GitLabDiscussionSchema).parse(data);
} }
/**
* List discussions for an issue
*
* @param {string} projectId - The ID or URL-encoded path of the project
* @param {number} issueIid - The internal ID of the project issue
* @param {Object} options - Pagination and sorting options
* @returns {Promise<GitLabDiscussion[]>} List of issue discussions
*/
async function listIssueDiscussions(
projectId: string,
issueIid: number,
options: {
page?: number,
per_page?: number,
sort?: "asc" | "desc",
order_by?: "created_at" | "updated_at"
} = {}
): Promise<GitLabDiscussion[]> {
projectId = decodeURIComponent(projectId); // Decode project ID
const url = new URL(
`${GITLAB_API_URL}/projects/${encodeURIComponent(
projectId
)}/issues/${issueIid}/discussions`
);
// Add query parameters for pagination and sorting
if (options.page) {
url.searchParams.append("page", options.page.toString());
}
if (options.per_page) {
url.searchParams.append("per_page", options.per_page.toString());
}
if (options.sort) {
url.searchParams.append("sort", options.sort);
}
if (options.order_by) {
url.searchParams.append("order_by", options.order_by);
}
const response = await fetch(url.toString(), {
...DEFAULT_FETCH_CONFIG,
});
await handleGitLabError(response);
const data = await response.json();
// Parse the response as an array of discussions
return z.array(GitLabDiscussionSchema).parse(data);
}
/** /**
* Modify an existing merge request thread note * Modify an existing merge request thread note
* 병합 요청 토론 노트 수정 * 병합 요청 토론 노트 수정
@ -2647,6 +2704,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
}; };
} }
case "list_issue_discussions": {
const args = ListIssueDiscussionsSchema.parse(request.params.arguments);
const { project_id, issue_iid, ...options } = args;
const discussions = await listIssueDiscussions(project_id, issue_iid, options);
return {
content: [{ type: "text", text: JSON.stringify(discussions, null, 2) }],
};
}
case "get_issue_link": { case "get_issue_link": {
const args = GetIssueLinkSchema.parse(request.params.arguments); const args = GetIssueLinkSchema.parse(request.params.arguments);
const link = await getIssueLink( const link = await getIssueLink(

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "@zereight/mcp-gitlab", "name": "@zereight/mcp-gitlab",
"version": "1.0.36", "version": "1.0.38",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@zereight/mcp-gitlab", "name": "@zereight/mcp-gitlab",
"version": "1.0.36", "version": "1.0.38",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@modelcontextprotocol/sdk": "1.8.0", "@modelcontextprotocol/sdk": "1.8.0",

View File

@ -1,6 +1,6 @@
{ {
"name": "@zereight/mcp-gitlab", "name": "@zereight/mcp-gitlab",
"version": "1.0.38", "version": "1.0.39",
"description": "MCP server for using the GitLab API", "description": "MCP server for using the GitLab API",
"license": "MIT", "license": "MIT",
"author": "zereight", "author": "zereight",

6
release-notes.md Normal file
View File

@ -0,0 +1,6 @@
### Fixed
- Added `expanded` property to `start` and `end` in `GitLabDiscussionNoteSchema`
Now you can expand or collapse more information at the start and end of discussion notes.
Example: In code review, you can choose to show or hide specific parts of the discussion.
(See: [PR #40](https://github.com/zereight/gitlab-mcp/pull/40))

View File

@ -107,7 +107,7 @@ export const GitLabRepositorySchema = z.object({
jobs_enabled: z.boolean().optional(), jobs_enabled: z.boolean().optional(),
snippets_enabled: z.boolean().optional(), snippets_enabled: z.boolean().optional(),
can_create_merge_request_in: z.boolean().optional(), can_create_merge_request_in: z.boolean().optional(),
resolve_outdated_diff_discussions: z.boolean().optional(), resolve_outdated_diff_discussions: z.boolean().nullable().optional(),
shared_runners_enabled: z.boolean().optional(), shared_runners_enabled: z.boolean().optional(),
shared_with_groups: z shared_with_groups: z
.array( .array(
@ -757,6 +757,15 @@ export const ListIssueLinksSchema = z.object({
issue_iid: z.number().describe("The internal ID of a project's issue"), issue_iid: z.number().describe("The internal ID of a project's issue"),
}); });
export const ListIssueDiscussionsSchema = z.object({
project_id: z.string().describe("Project ID or URL-encoded path"),
issue_iid: z.number().describe("The internal ID of the project issue"),
page: z.number().optional().describe("Page number for pagination"),
per_page: z.number().optional().describe("Number of items per page"),
sort: z.enum(["asc", "desc"]).optional().describe("Return issue discussions sorted in ascending or descending order"),
order_by: z.enum(["created_at", "updated_at"]).optional().describe("Return issue discussions ordered by created_at or updated_at fields"),
});
export const GetIssueLinkSchema = z.object({ export const GetIssueLinkSchema = z.object({
project_id: z.string().describe("Project ID or URL-encoded path"), project_id: z.string().describe("Project ID or URL-encoded path"),
issue_iid: z.number().describe("The internal ID of a project's issue"), issue_iid: z.number().describe("The internal ID of a project's issue"),
@ -1075,6 +1084,7 @@ export type GitLabMergeRequestDiff = z.infer<
>; >;
export type CreateNoteOptions = z.infer<typeof CreateNoteSchema>; export type CreateNoteOptions = z.infer<typeof CreateNoteSchema>;
export type GitLabIssueLink = z.infer<typeof GitLabIssueLinkSchema>; export type GitLabIssueLink = z.infer<typeof GitLabIssueLinkSchema>;
export type ListIssueDiscussionsOptions = z.infer<typeof ListIssueDiscussionsSchema>;
export type GitLabNamespace = z.infer<typeof GitLabNamespaceSchema>; export type GitLabNamespace = z.infer<typeof GitLabNamespaceSchema>;
export type GitLabNamespaceExistsResponse = z.infer< export type GitLabNamespaceExistsResponse = z.infer<
typeof GitLabNamespaceExistsResponseSchema typeof GitLabNamespaceExistsResponseSchema