Compare commits

...

41 Commits

Author SHA1 Message Date
8ba33986f3 [main] docs: update changelog for v1.0.45 pipeline tools
🚀 Breaking Changes:
- None

📝 Details:
- Added 5 new pipeline management tools
- Pipeline status monitoring and analysis support
2025-05-24 21:02:58 +09:00
64a936446e [release] feat: update version to 1.0.45
🚀 Breaking Changes:
- Version updated from 1.0.44 to 1.0.45
2025-05-24 20:57:15 +09:00
8ab0ac7145 Merge pull request #52 from vicendominguez/main
feat(release): 1.0.44  adds pipeline jobs tool
2025-05-24 20:55:55 +09:00
ea06c21f29 feat(release): 1.0.44 adds pipeline jobs tool 2025-05-24 13:31:47 +02:00
140620397b chore(release): 1.0.43 - get_repository_tree is added read_only_mode 2025-05-23 00:41:56 +09:00
3d7aa8035d docs: translate issue notes changelog from Korean to English 2025-05-22 21:28:34 +09:00
25be1947b9 chore(release): 1.0.42 - issue note 기능 추가 (#47) 2025-05-22 21:24:29 +09:00
864ee77ae6 Merge pull request #47 from svengt/feat/add-issue-notes-support
feat: add support for creating and updating issue notes
2025-05-22 21:22:04 +09:00
dc6cc59434 feat: add support for creating and updating issue notes
- Added create_issue_note to add a new note to an existing issue thread
- Added update_issue_note to modify an existing issue thread note
- Similar to existing merge request note functions but for issues
2025-05-22 13:25:31 +02:00
5924fd3ed4 Merge pull request #45 from vlucaswang/fix/docs
fix: fix README
2025-05-21 14:28:08 +09:00
f4b265bf2e fix: fix README 2025-05-21 14:35:37 +09:30
b326f4c3c3 docs: update release notes for v1.0.40 (2025-05-21) 2025-05-21 03:40:02 +09:00
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
0bb59a3217 Bump version 2025-05-17 15:38:18 +09:00
b908f03801 Merge pull request #40 from huerlisi/fix/discussion-enum
fix: add `expanded` to `start` and `end` for GitLabDiscussionNoteSchema
2025-05-17 15:36:11 +09:00
5024a2a5af fix: add expanded to start and end for GitLabDiscussionNoteSchema 2025-05-16 21:36:21 +02:00
d2cced1b38 Bump version 2025-05-15 10:53:14 +09:00
2fec95d469 Merge pull request #38 from ncrum/feat/merge-request-note
Adds threaded comment support for merge requests
2025-05-15 10:51:48 +09:00
3565d1b397 Merge pull request #37 from ncrum/fix/resolve-notes
Support resolving merge request discussion notes
2025-05-15 10:50:39 +09:00
353e62a401 refactor: rename add_merge_request_thread_note to create_merge_request_note for consistency 2025-05-13 16:52:17 -06:00
3f2b35535e feat: Implement add_merge_request_thread_note function for adding notes to existing MR threads 2025-05-13 16:20:21 -06:00
026dd58887 feat: Add create_merge_request_thread tool for diff notes
- Implement new tool for creating MR threads with positioning support
- Create schemas to handle diff notes with file and line number positions
- Support optional created_at timestamp parameter
- Update README with the new tool information
2025-05-13 15:45:43 -06:00
bde83c0a91 feat: support resolving merge request notes 2025-05-13 14:54:05 -06:00
08ab1357a0 feat: Decode project_id for GitLab API calls
- Decode project_id using decodeURIComponent() in relevant helper functions.
- This resolves API call issues related to project ID encoding differences between models.
- Updated CHANGELOG for 1.0.36 and added thanks to Aubermean.
2025-05-13 02:20:59 +09:00
651072dfd7 [main] chore: update version to 1.0.35 🚀
📝 Details:
- Incremented package version from 1.0.34 to 1.0.35
2025-05-13 01:53:10 +09:00
bf250b0d88 [main] refactor: update label_id schema to use string type
🚀 Breaking Changes:
- label_id now requires a string type instead of a union of number and string.

📝 Details:
- Improved consistency in schema definitions for label handling.
2025-05-13 01:53:00 +09:00
3a25e7c5e8 [main] docs: update README with detailed descriptions for merge request functions
📝 Details:
- Added clarifications for `get_merge_request`, `get_merge_request_diffs`, and `update_merge_request` functions.
- Introduced `get_repository_tree` function to the documentation.
2025-05-07 21:46:35 +09:00
23a9bbc728 [main] chore: update version to 1.0.34
🚀 Breaking Changes:
- Updated package version from 1.0.33 to 1.0.34
2025-05-07 21:45:59 +09:00
5398526f94 Merge pull request #35 from BobMerkus/feat/list-repository
feat: Gitlab list repository tree tool
2025-05-07 21:45:35 +09:00
Bob
bccd5f29c3 feat: Gitlab list repository tree tool 2025-05-07 14:11:37 +02:00
8071fef6c4 [main] chore: package.json 버전 1.0.33으로 업데이트
🚀 Breaking Changes:
- 없음
2025-05-01 08:33:56 +09:00
f0eac83788 Merge pull request #34 from nkaewam/main
feat: support search by branch for get_merge_request
2025-05-01 08:33:29 +09:00
7b8cbc0806 fix: rename to source branch 2025-04-30 15:44:21 +07:00
20f62756c1 feat: support search by branch for get_merge_request 2025-04-30 15:41:51 +07:00
8 changed files with 1594 additions and 254 deletions

35
CHANGELOG.md Normal file
View File

@ -0,0 +1,35 @@
## [1.0.45] - 2025-05-24
### Added
- 🔄 **Pipeline Management Tools**: Added GitLab pipeline status monitoring and management functionality
- `list_pipelines`: List project pipelines with various filtering options
- `get_pipeline`: Get detailed information about a specific pipeline
- `list_pipeline_jobs`: List all jobs in a specific pipeline
- `get_pipeline_job`: Get detailed information about a specific pipeline job
- `get_pipeline_job_output`: Get execution logs/output from pipeline jobs
- 📊 Pipeline status summary and analysis support
- Example: "How many of the last N pipelines are successful?"
- Example: "Can you make a summary of the output in the last pipeline?"
- See: [PR #52](https://github.com/zereight/gitlab-mcp/pull/52)
---
## [1.0.42] - 2025-05-22
### Added
- Added support for creating and updating issue notes (comments) in GitLab.
- You can now add or edit comments on issues.
- See: [PR #47](https://github.com/zereight/gitlab-mcp/pull/47)
---
## [1.0.38] - 2025-05-17
### 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))

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"]

106
README.md
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": {
@ -24,7 +26,39 @@ When using with the Claude App, you need to set up your API key and URLs directl
"GITLAB_PERSONAL_ACCESS_TOKEN": "your_gitlab_token", "GITLAB_PERSONAL_ACCESS_TOKEN": "your_gitlab_token",
"GITLAB_API_URL": "your_gitlab_api_url", "GITLAB_API_URL": "your_gitlab_api_url",
"GITLAB_READ_ONLY_MODE": "false", "GITLAB_READ_ONLY_MODE": "false",
"USE_GITLAB_WIKI":"true" "USE_GITLAB_WIKI": "true"
}
}
}
}
```
#### 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/gitlab-mcp"
],
"env": {
"GITLAB_PERSONAL_ACCESS_TOKEN": "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"
} }
} }
} }
@ -33,7 +67,6 @@ When using with the Claude App, you need to set up your API key and URLs directl
### Environment Variables ### 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`)
- `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. - `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.
@ -51,34 +84,45 @@ When using with the Claude App, you need to set up your API key and URLs directl
7. `create_merge_request` - Create a new merge request in a GitLab project 7. `create_merge_request` - Create a new merge request in a GitLab project
8. `fork_repository` - Fork a GitLab project to your account or specified namespace 8. `fork_repository` - Fork a GitLab project to your account or specified namespace
9. `create_branch` - Create a new branch in a GitLab project 9. `create_branch` - Create a new branch in a GitLab project
10. `get_merge_request` - Get details of a merge request 10. `get_merge_request` - Get details of a merge request (Either mergeRequestIid or branchName must be provided)
11. `get_merge_request_diffs` - Get the changes/diffs of a merge request 11. `get_merge_request_diffs` - Get the changes/diffs of a merge request (Either mergeRequestIid or branchName must be provided)
12. `update_merge_request` - Update a merge request 12. `update_merge_request` - Update a merge request (Either mergeRequestIid or branchName must be provided)
13. `create_note` - Create a new note (comment) to an issue or merge request 13. `create_note` - Create a new note (comment) to an issue or merge request
14. `mr_discussions` - List discussion items for a merge request 14. `create_merge_request_thread` - Create a new thread on a merge request
15. `update_merge_request_note` - Modify an existing merge request thread note 15. `mr_discussions` - List discussion items for a merge request
16. `list_issues` - List issues in a GitLab project with filtering options 16. `update_merge_request_note` - Modify an existing merge request thread note
17. `get_issue` - Get details of a specific issue in a GitLab project 17. `create_merge_request_note` - Add a new note to an existing merge request thread
18. `update_issue` - Update an issue in a GitLab project 18. `update_issue_note` - Modify an existing issue thread note
19. `delete_issue` - Delete an issue from a GitLab project 19. `create_issue_note` - Add a new note to an existing issue thread
20. `list_issue_links` - List all issue links for a specific issue 20. `list_issues` - List issues in a GitLab project with filtering options
21. `get_issue_link` - Get a specific issue link 21. `get_issue` - Get details of a specific issue in a GitLab project
22. `create_issue_link` - Create an issue link between two issues 22. `update_issue` - Update an issue in a GitLab project
23. `delete_issue_link` - Delete an issue link 23. `delete_issue` - Delete an issue from a GitLab project
24. `list_namespaces` - List all namespaces available to the current user 24. `list_issue_links` - List all issue links for a specific issue
25. `get_namespace` - Get details of a namespace by ID or path 25. `list_issue_discussions` - List discussions for an issue in a GitLab project
26. `verify_namespace` - Verify if a namespace path exists 26. `get_issue_link` - Get a specific issue link
27. `get_project` - Get details of a specific project 27. `create_issue_link` - Create an issue link between two issues
28. `list_projects` - List projects accessible by the current user 28. `delete_issue_link` - Delete an issue link
29. `list_labels` - List labels for a project 29. `list_namespaces` - List all namespaces available to the current user
30. `get_label` - Get a single label from a project 30. `get_namespace` - Get details of a namespace by ID or path
31. `create_label` - Create a new label in a project 31. `verify_namespace` - Verify if a namespace path exists
32. `update_label` - Update an existing label in a project 32. `get_project` - Get details of a specific project
33. `delete_label` - Delete a label from a project 33. `list_projects` - List projects accessible by the current user
34. `list_group_projects` - List projects in a GitLab group with filtering options 34. `list_labels` - List labels for a project
35. `list_wiki_pages` - List wiki pages in a GitLab project 35. `get_label` - Get a single label from a project
36. `get_wiki_page` - Get details of a specific wiki page 36. `create_label` - Create a new label in a project
37. `create_wiki_page` - Create a new wiki page in a GitLab project 37. `update_label` - Update an existing label in a project
38. `update_wiki_page` - Update an existing wiki page in a GitLab project 38. `delete_label` - Delete a label from a project
39. `delete_wiki_page` - Delete a wiki page from a GitLab project 39. `list_group_projects` - List projects in a GitLab group with filtering options
40. `list_wiki_pages` - List wiki pages in a GitLab project
41. `get_wiki_page` - Get details of a specific wiki page
42. `create_wiki_page` - Create a new wiki page in a GitLab project
43. `update_wiki_page` - Update an existing wiki page in a GitLab project
44. `delete_wiki_page` - Delete a wiki page from a GitLab project
45. `get_repository_tree` - Get the repository tree for a GitLab project (list files and directories)
46. `list_pipelines` - List pipelines in a GitLab project with filtering options
47. `get_pipeline` - Get details of a specific pipeline in a GitLab project
48. `list_pipeline_jobs` - List all jobs in a specific pipeline
49. `get_pipeline_job` - Get details of a GitLab pipeline job number
50. `get_pipeline_job_output` - Get the output/trace of a GitLab pipeline job number
<!-- TOOLS-END --> <!-- TOOLS-END -->

976
index.ts

File diff suppressed because it is too large Load Diff

5
package-lock.json generated
View File

@ -1,16 +1,17 @@
{ {
"name": "@zereight/mcp-gitlab", "name": "@zereight/mcp-gitlab",
"version": "1.0.30", "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.30", "version": "1.0.38",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@modelcontextprotocol/sdk": "1.8.0", "@modelcontextprotocol/sdk": "1.8.0",
"@types/node-fetch": "^2.6.12", "@types/node-fetch": "^2.6.12",
"form-data": "^4.0.0",
"http-proxy-agent": "^7.0.2", "http-proxy-agent": "^7.0.2",
"https-proxy-agent": "^7.0.6", "https-proxy-agent": "^7.0.6",
"node-fetch": "^3.3.2", "node-fetch": "^3.3.2",

View File

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

5
release-notes.md Normal file
View File

@ -0,0 +1,5 @@
### 1.0.40 (2025-05-21)
- Added support for listing discussions (comments/notes) on GitLab issues.
- Example: You can now easily fetch all conversations (comments) attached to an issue via the API.
- Related PR: [#44](https://github.com/zereight/gitlab-mcp/pull/44)

View File

@ -7,6 +7,119 @@ export const GitLabAuthorSchema = z.object({
date: z.string(), date: z.string(),
}); });
// Pipeline related schemas
export const GitLabPipelineSchema = z.object({
id: z.number(),
project_id: z.number(),
sha: z.string(),
ref: z.string(),
status: z.string(),
source: z.string().optional(),
created_at: z.string(),
updated_at: z.string(),
web_url: z.string(),
duration: z.number().nullable().optional(),
started_at: z.string().nullable().optional(),
finished_at: z.string().nullable().optional(),
coverage: z.number().nullable().optional(),
user: z.object({
id: z.number(),
name: z.string(),
username: z.string(),
avatar_url: z.string().nullable().optional(),
}).optional(),
detailed_status: z.object({
icon: z.string().optional(),
text: z.string().optional(),
label: z.string().optional(),
group: z.string().optional(),
tooltip: z.string().optional(),
has_details: z.boolean().optional(),
details_path: z.string().optional(),
illustration: z.object({
image: z.string().optional(),
size: z.string().optional(),
title: z.string().optional(),
}).optional(),
favicon: z.string().optional(),
}).optional(),
});
// Pipeline job related schemas
export const GitLabPipelineJobSchema = z.object({
id: z.number(),
status: z.string(),
stage: z.string(),
name: z.string(),
ref: z.string(),
tag: z.boolean(),
coverage: z.number().nullable().optional(),
created_at: z.string(),
started_at: z.string().nullable().optional(),
finished_at: z.string().nullable().optional(),
duration: z.number().nullable().optional(),
user: z.object({
id: z.number(),
name: z.string(),
username: z.string(),
avatar_url: z.string().nullable().optional(),
}).optional(),
commit: z.object({
id: z.string(),
short_id: z.string(),
title: z.string(),
author_name: z.string(),
author_email: z.string(),
}).optional(),
pipeline: z.object({
id: z.number(),
project_id: z.number(),
status: z.string(),
ref: z.string(),
sha: z.string(),
}).optional(),
web_url: z.string().optional(),
});
// Schema for listing pipelines
export const ListPipelinesSchema = z.object({
project_id: z.string().describe("Project ID or URL-encoded path"),
scope: z.enum(['running', 'pending', 'finished', 'branches', 'tags']).optional().describe("The scope of pipelines"),
status: z.enum(['created', 'waiting_for_resource', 'preparing', 'pending', 'running', 'success', 'failed', 'canceled', 'skipped', 'manual', 'scheduled']).optional().describe("The status of pipelines"),
ref: z.string().optional().describe("The ref of pipelines"),
sha: z.string().optional().describe("The SHA of pipelines"),
yaml_errors: z.boolean().optional().describe("Returns pipelines with invalid configurations"),
username: z.string().optional().describe("The username of the user who triggered pipelines"),
updated_after: z.string().optional().describe("Return pipelines updated after the specified date"),
updated_before: z.string().optional().describe("Return pipelines updated before the specified date"),
order_by: z.enum(['id', 'status', 'ref', 'updated_at', 'user_id']).optional().describe("Order pipelines by"),
sort: z.enum(['asc', 'desc']).optional().describe("Sort pipelines"),
page: z.number().optional().describe("Page number for pagination"),
per_page: z.number().optional().describe("Number of items per page (max 100)"),
});
// Schema for getting a specific pipeline
export const GetPipelineSchema = z.object({
project_id: z.string().describe("Project ID or URL-encoded path"),
pipeline_id: z.number().describe("The ID of the pipeline"),
});
// Schema for listing jobs in a pipeline
export const ListPipelineJobsSchema = z.object({
project_id: z.string().describe("Project ID or URL-encoded path"),
pipeline_id: z.number().describe("The ID of the pipeline"),
scope: z.enum(['created', 'pending', 'running', 'failed', 'success', 'canceled', 'skipped', 'manual']).optional().describe("The scope of jobs to show"),
include_retried: z.boolean().optional().describe("Whether to include retried jobs"),
page: z.number().optional().describe("Page number for pagination"),
per_page: z.number().optional().describe("Number of items per page (max 100)"),
});
// Schema for the input parameters for pipeline job operations
export const GetPipelineJobOutputSchema = z.object({
project_id: z.string().describe("Project ID or URL-encoded path"),
job_id: z.number().describe("The ID of the job"),
});
// Namespace related schemas // Namespace related schemas
// Base schema for project-related operations // Base schema for project-related operations
@ -63,15 +176,17 @@ export const GitLabRepositorySchema = z.object({
created_at: z.string().optional(), created_at: z.string().optional(),
last_activity_at: z.string().optional(), last_activity_at: z.string().optional(),
default_branch: z.string().optional(), default_branch: z.string().optional(),
namespace: z.object({ namespace: z
id: z.number(), .object({
name: z.string(), id: z.number(),
path: z.string(), name: z.string(),
kind: z.string(), path: z.string(),
full_path: z.string(), kind: z.string(),
avatar_url: z.string().nullable().optional(), full_path: z.string(),
web_url: z.string().optional(), avatar_url: z.string().nullable().optional(),
}).optional(), web_url: z.string().optional(),
})
.optional(),
readme_url: z.string().optional().nullable(), readme_url: z.string().optional().nullable(),
topics: z.array(z.string()).optional(), topics: z.array(z.string()).optional(),
tag_list: z.array(z.string()).optional(), // deprecated but still present tag_list: z.array(z.string()).optional(), // deprecated but still present
@ -79,16 +194,24 @@ export const GitLabRepositorySchema = z.object({
archived: z.boolean().optional(), archived: z.boolean().optional(),
forks_count: z.number().optional(), forks_count: z.number().optional(),
star_count: z.number().optional(), star_count: z.number().optional(),
permissions: z.object({ permissions: z
project_access: z.object({ .object({
access_level: z.number(), project_access: z
notification_level: z.number().optional(), .object({
}).optional().nullable(), access_level: z.number(),
group_access: z.object({ notification_level: z.number().optional(),
access_level: z.number(), })
notification_level: z.number().optional(), .optional()
}).optional().nullable(), .nullable(),
}).optional(), group_access: z
.object({
access_level: z.number(),
notification_level: z.number().optional(),
})
.optional()
.nullable(),
})
.optional(),
container_registry_enabled: z.boolean().optional(), container_registry_enabled: z.boolean().optional(),
container_registry_access_level: z.string().optional(), container_registry_access_level: z.string().optional(),
issues_enabled: z.boolean().optional(), issues_enabled: z.boolean().optional(),
@ -97,14 +220,18 @@ 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.array(z.object({ shared_with_groups: z
group_id: z.number(), .array(
group_name: z.string(), z.object({
group_full_path: z.string(), group_id: z.number(),
group_access_level: z.number(), group_name: z.string(),
})).optional(), group_full_path: z.string(),
group_access_level: z.number(),
})
)
.optional(),
}); });
// Project schema (extended from repository schema) // Project schema (extended from repository schema)
@ -146,17 +273,41 @@ export const FileOperationSchema = z.object({
}); });
// Tree and commit schemas // Tree and commit schemas
export const GitLabTreeEntrySchema = z.object({ export const GitLabTreeItemSchema = z.object({
id: z.string(), // Changed from sha to match GitLab API id: z.string(),
name: z.string(), name: z.string(),
type: z.enum(["blob", "tree"]), type: z.enum(["tree", "blob"]),
path: z.string(), path: z.string(),
mode: z.string(), mode: z.string(),
}); });
export const GetRepositoryTreeSchema = z.object({
project_id: z.string().describe("The ID or URL-encoded path of the project"),
path: z.string().optional().describe("The path inside the repository"),
ref: z
.string()
.optional()
.describe(
"The name of a repository branch or tag. Defaults to the default branch."
),
recursive: z
.boolean()
.optional()
.describe("Boolean value to get a recursive tree"),
per_page: z
.number()
.optional()
.describe("Number of results to show per page"),
page_token: z
.string()
.optional()
.describe("The tree record ID for pagination"),
pagination: z.string().optional().describe("Pagination method (keyset)"),
});
export const GitLabTreeSchema = z.object({ export const GitLabTreeSchema = z.object({
id: z.string(), // Changed from sha to match GitLab API id: z.string(), // Changed from sha to match GitLab API
tree: z.array(GitLabTreeEntrySchema), tree: z.array(GitLabTreeItemSchema),
}); });
export const GitLabCommitSchema = z.object({ export const GitLabCommitSchema = z.object({
@ -276,17 +427,21 @@ export const GitLabIssueSchema = z.object({
updated_at: z.string(), updated_at: z.string(),
closed_at: z.string().nullable(), closed_at: z.string().nullable(),
web_url: z.string(), // Changed from html_url to match GitLab API web_url: z.string(), // Changed from html_url to match GitLab API
references: z.object({ references: z
short: z.string(), .object({
relative: z.string(), short: z.string(),
full: z.string(), relative: z.string(),
}).optional(), full: z.string(),
time_stats: z.object({ })
time_estimate: z.number(), .optional(),
total_time_spent: z.number(), time_stats: z
human_time_estimate: z.string().nullable(), .object({
human_total_time_spent: z.string().nullable(), time_estimate: z.number(),
}).optional(), total_time_spent: z.number(),
human_time_estimate: z.string().nullable(),
human_total_time_spent: z.string().nullable(),
})
.optional(),
confidential: z.boolean().optional(), confidential: z.boolean().optional(),
due_date: z.string().nullable().optional(), due_date: z.string().nullable().optional(),
discussion_locked: z.boolean().nullable().optional(), discussion_locked: z.boolean().nullable().optional(),
@ -296,7 +451,7 @@ export const GitLabIssueSchema = z.object({
// NEW SCHEMA: For issue with link details (used in listing issue links) // NEW SCHEMA: For issue with link details (used in listing issue links)
export const GitLabIssueWithLinkDetailsSchema = GitLabIssueSchema.extend({ export const GitLabIssueWithLinkDetailsSchema = GitLabIssueSchema.extend({
issue_link_id: z.number(), issue_link_id: z.number(),
link_type: z.enum(['relates_to', 'blocks', 'is_blocked_by']), link_type: z.enum(["relates_to", "blocks", "is_blocked_by"]),
link_created_at: z.string(), link_created_at: z.string(),
link_updated_at: z.string(), link_updated_at: z.string(),
}); });
@ -305,11 +460,13 @@ export const GitLabIssueWithLinkDetailsSchema = GitLabIssueSchema.extend({
export const GitLabForkParentSchema = z.object({ export const GitLabForkParentSchema = z.object({
name: z.string(), name: z.string(),
path_with_namespace: z.string(), // Changed from full_name to match GitLab API path_with_namespace: z.string(), // Changed from full_name to match GitLab API
owner: z.object({ owner: z
username: z.string(), // Changed from login to match GitLab API .object({
id: z.number(), username: z.string(), // Changed from login to match GitLab API
avatar_url: z.string(), id: z.number(),
}).optional(), // Made optional to handle cases where GitLab API doesn't include it avatar_url: z.string(),
})
.optional(), // Made optional to handle cases where GitLab API doesn't include it
web_url: z.string(), // Changed from html_url to match GitLab API web_url: z.string(), // Changed from html_url to match GitLab API
}); });
@ -377,34 +534,40 @@ export const GitLabDiscussionNoteSchema = z.object({
resolved: z.boolean().optional(), resolved: z.boolean().optional(),
resolved_by: GitLabUserSchema.nullable().optional(), resolved_by: GitLabUserSchema.nullable().optional(),
resolved_at: z.string().nullable().optional(), resolved_at: z.string().nullable().optional(),
position: z.object({ // Only present for DiffNote position: z
base_sha: z.string(), .object({
start_sha: z.string(), // Only present for DiffNote
head_sha: z.string(), base_sha: z.string(),
old_path: z.string(), start_sha: z.string(),
new_path: z.string(), head_sha: z.string(),
position_type: z.enum(["text", "image", "file"]), old_path: z.string(),
old_line: z.number().nullable(), new_path: z.string(),
new_line: z.number().nullable(), position_type: z.enum(["text", "image", "file"]),
line_range: z.object({ old_line: z.number().nullable(),
start: z.object({ new_line: z.number().nullable(),
line_code: z.string(), line_range: z
type: z.enum(["new", "old"]), .object({
old_line: z.number().nullable(), start: z.object({
new_line: z.number().nullable(), line_code: z.string(),
}), type: z.enum(["new", "old", "expanded"]),
end: z.object({ old_line: z.number().nullable(),
line_code: z.string(), new_line: z.number().nullable(),
type: z.enum(["new", "old"]), }),
old_line: z.number().nullable(), end: z.object({
new_line: z.number().nullable(), line_code: z.string(),
}), type: z.enum(["new", "old", "expanded"]),
}).nullable().optional(), // For multi-line diff notes old_line: z.number().nullable(),
width: z.number().optional(), // For image diff notes new_line: z.number().nullable(),
height: z.number().optional(), // For image diff notes }),
x: z.number().optional(), // For image diff notes })
y: z.number().optional(), // For image diff notes .nullable()
}).optional(), .optional(), // For multi-line diff notes
width: z.number().optional(), // For image diff notes
height: z.number().optional(), // For image diff notes
x: z.number().optional(), // For image diff notes
y: z.number().optional(), // For image diff notes
})
.optional(),
}); });
export type GitLabDiscussionNote = z.infer<typeof GitLabDiscussionNoteSchema>; export type GitLabDiscussionNote = z.infer<typeof GitLabDiscussionNoteSchema>;
@ -425,8 +588,36 @@ export const UpdateMergeRequestNoteSchema = ProjectParamsSchema.extend({
merge_request_iid: z.number().describe("The IID of a merge request"), merge_request_iid: z.number().describe("The IID of a merge request"),
discussion_id: z.string().describe("The ID of a thread"), discussion_id: z.string().describe("The ID of a thread"),
note_id: z.number().describe("The ID of a thread note"), note_id: z.number().describe("The ID of a thread note"),
body: z.string().optional().describe("The content of the note or reply"),
resolved: z.boolean().optional().describe("Resolve or unresolve the note"),
}).refine(data => data.body !== undefined || data.resolved !== undefined, {
message: "At least one of 'body' or 'resolved' must be provided"
}).refine(data => !(data.body !== undefined && data.resolved !== undefined), {
message: "Only one of 'body' or 'resolved' can be provided, not both"
});
// Input schema for adding a note to an existing merge request discussion
export const CreateMergeRequestNoteSchema = ProjectParamsSchema.extend({
merge_request_iid: z.number().describe("The IID of a merge request"),
discussion_id: z.string().describe("The ID of a thread"),
body: z.string().describe("The content of the note or reply"), body: z.string().describe("The content of the note or reply"),
resolved: z.boolean().optional().describe("Resolve or unresolve the note"), // Optional based on API docs created_at: z.string().optional().describe("Date the note was created at (ISO 8601 format)"),
});
// Input schema for updating an issue discussion note
export const UpdateIssueNoteSchema = ProjectParamsSchema.extend({
issue_iid: z.number().describe("The IID of an issue"),
discussion_id: z.string().describe("The ID of a thread"),
note_id: z.number().describe("The ID of a thread note"),
body: z.string().describe("The content of the note or reply"),
});
// Input schema for adding a note to an existing issue discussion
export const CreateIssueNoteSchema = ProjectParamsSchema.extend({
issue_iid: z.number().describe("The IID of an issue"),
discussion_id: z.string().describe("The ID of a thread"),
body: z.string().describe("The content of the note or reply"),
created_at: z.string().optional().describe("Date the note was created at (ISO 8601 format)"),
}); });
// API Operation Parameter Schemas // API Operation Parameter Schemas
@ -440,10 +631,7 @@ export const CreateOrUpdateFileSchema = ProjectParamsSchema.extend({
.string() .string()
.optional() .optional()
.describe("Path of the file to move/rename"), .describe("Path of the file to move/rename"),
last_commit_id: z last_commit_id: z.string().optional().describe("Last known file commit ID"),
.string()
.optional()
.describe("Last known file commit ID"),
commit_id: z commit_id: z
.string() .string()
.optional() .optional()
@ -539,7 +727,9 @@ export const GitLabMergeRequestDiffSchema = z.object({
export const GetMergeRequestSchema = ProjectParamsSchema.extend({ export const GetMergeRequestSchema = ProjectParamsSchema.extend({
merge_request_iid: z merge_request_iid: z
.number() .number()
.describe("The internal ID of the merge request"), .optional()
.describe("The IID of a merge request"),
source_branch: z.string().optional().describe("Source branch name"),
}); });
export const UpdateMergeRequestSchema = GetMergeRequestSchema.extend({ export const UpdateMergeRequestSchema = GetMergeRequestSchema.extend({
@ -585,22 +775,61 @@ export const CreateNoteSchema = z.object({
// Issues API operation schemas // Issues API operation schemas
export const ListIssuesSchema = z.object({ export const ListIssuesSchema = z.object({
project_id: z.string().describe("Project ID or URL-encoded path"), project_id: z.string().describe("Project ID or URL-encoded path"),
assignee_id: z.number().optional().describe("Return issues assigned to the given user ID"), assignee_id: z
assignee_username: z.string().optional().describe("Return issues assigned to the given username"), .number()
author_id: z.number().optional().describe("Return issues created by the given user ID"), .optional()
author_username: z.string().optional().describe("Return issues created by the given username"), .describe("Return issues assigned to the given user ID"),
confidential: z.boolean().optional().describe("Filter confidential or public issues"), assignee_username: z
created_after: z.string().optional().describe("Return issues created after the given time"), .string()
created_before: z.string().optional().describe("Return issues created before the given time"), .optional()
due_date: z.string().optional().describe("Return issues that have the due date"), .describe("Return issues assigned to the given username"),
author_id: z
.number()
.optional()
.describe("Return issues created by the given user ID"),
author_username: z
.string()
.optional()
.describe("Return issues created by the given username"),
confidential: z
.boolean()
.optional()
.describe("Filter confidential or public issues"),
created_after: z
.string()
.optional()
.describe("Return issues created after the given time"),
created_before: z
.string()
.optional()
.describe("Return issues created before the given time"),
due_date: z
.string()
.optional()
.describe("Return issues that have the due date"),
label_name: z.array(z.string()).optional().describe("Array of label names"), label_name: z.array(z.string()).optional().describe("Array of label names"),
milestone: z.string().optional().describe("Milestone title"), milestone: z.string().optional().describe("Milestone title"),
scope: z.enum(['created-by-me', 'assigned-to-me', 'all']).optional().describe("Return issues from a specific scope"), scope: z
.enum(["created-by-me", "assigned-to-me", "all"])
.optional()
.describe("Return issues from a specific scope"),
search: z.string().optional().describe("Search for specific terms"), search: z.string().optional().describe("Search for specific terms"),
state: z.enum(['opened', 'closed', 'all']).optional().describe("Return issues with a specific state"), state: z
updated_after: z.string().optional().describe("Return issues updated after the given time"), .enum(["opened", "closed", "all"])
updated_before: z.string().optional().describe("Return issues updated before the given time"), .optional()
with_labels_details: z.boolean().optional().describe("Return more details for each label"), .describe("Return issues with a specific state"),
updated_after: z
.string()
.optional()
.describe("Return issues updated after the given time"),
updated_before: z
.string()
.optional()
.describe("Return issues updated before the given time"),
with_labels_details: z
.boolean()
.optional()
.describe("Return more details for each label"),
page: z.number().optional().describe("Page number for pagination"), page: z.number().optional().describe("Page number for pagination"),
per_page: z.number().optional().describe("Number of items per page"), per_page: z.number().optional().describe("Number of items per page"),
}); });
@ -615,13 +844,28 @@ export const UpdateIssueSchema = z.object({
issue_iid: z.number().describe("The internal ID of the project issue"), issue_iid: z.number().describe("The internal ID of the project issue"),
title: z.string().optional().describe("The title of the issue"), title: z.string().optional().describe("The title of the issue"),
description: z.string().optional().describe("The description of the issue"), description: z.string().optional().describe("The description of the issue"),
assignee_ids: z.array(z.number()).optional().describe("Array of user IDs to assign issue to"), assignee_ids: z
confidential: z.boolean().optional().describe("Set the issue to be confidential"), .array(z.number())
discussion_locked: z.boolean().optional().describe("Flag to lock discussions"), .optional()
due_date: z.string().optional().describe("Date the issue is due (YYYY-MM-DD)"), .describe("Array of user IDs to assign issue to"),
confidential: z
.boolean()
.optional()
.describe("Set the issue to be confidential"),
discussion_locked: z
.boolean()
.optional()
.describe("Flag to lock discussions"),
due_date: z
.string()
.optional()
.describe("Date the issue is due (YYYY-MM-DD)"),
labels: z.array(z.string()).optional().describe("Array of label names"), labels: z.array(z.string()).optional().describe("Array of label names"),
milestone_id: z.number().optional().describe("Milestone ID to assign"), milestone_id: z.number().optional().describe("Milestone ID to assign"),
state_event: z.enum(['close', 'reopen']).optional().describe("Update issue state (close/reopen)"), state_event: z
.enum(["close", "reopen"])
.optional()
.describe("Update issue state (close/reopen)"),
weight: z.number().optional().describe("Weight of the issue (0-9)"), weight: z.number().optional().describe("Weight of the issue (0-9)"),
}); });
@ -634,7 +878,7 @@ export const DeleteIssueSchema = z.object({
export const GitLabIssueLinkSchema = z.object({ export const GitLabIssueLinkSchema = z.object({
source_issue: GitLabIssueSchema, source_issue: GitLabIssueSchema,
target_issue: GitLabIssueSchema, target_issue: GitLabIssueSchema,
link_type: z.enum(['relates_to', 'blocks', 'is_blocked_by']), link_type: z.enum(["relates_to", "blocks", "is_blocked_by"]),
}); });
export const ListIssueLinksSchema = z.object({ export const ListIssueLinksSchema = z.object({
@ -642,6 +886,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"),
@ -651,9 +904,16 @@ export const GetIssueLinkSchema = z.object({
export const CreateIssueLinkSchema = z.object({ export const CreateIssueLinkSchema = 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"),
target_project_id: z.string().describe("The ID or URL-encoded path of a target project"), target_project_id: z
target_issue_iid: z.number().describe("The internal ID of a target project's issue"), .string()
link_type: z.enum(['relates_to', 'blocks', 'is_blocked_by']).optional().describe("The type of the relation, defaults to relates_to"), .describe("The ID or URL-encoded path of a target project"),
target_issue_iid: z
.number()
.describe("The internal ID of a target project's issue"),
link_type: z
.enum(["relates_to", "blocks", "is_blocked_by"])
.optional()
.describe("The type of the relation, defaults to relates_to"),
}); });
export const DeleteIssueLinkSchema = z.object({ export const DeleteIssueLinkSchema = z.object({
@ -667,7 +927,10 @@ export const ListNamespacesSchema = z.object({
search: z.string().optional().describe("Search term for namespaces"), search: z.string().optional().describe("Search term for namespaces"),
page: z.number().optional().describe("Page number for pagination"), page: z.number().optional().describe("Page number for pagination"),
per_page: z.number().optional().describe("Number of items per page"), per_page: z.number().optional().describe("Number of items per page"),
owned: z.boolean().optional().describe("Filter for namespaces owned by current user"), owned: z
.boolean()
.optional()
.describe("Filter for namespaces owned by current user"),
}); });
export const GetNamespaceSchema = z.object({ export const GetNamespaceSchema = z.object({
@ -687,73 +950,164 @@ export const ListProjectsSchema = z.object({
search: z.string().optional().describe("Search term for projects"), search: z.string().optional().describe("Search term for projects"),
page: z.number().optional().describe("Page number for pagination"), page: z.number().optional().describe("Page number for pagination"),
per_page: z.number().optional().describe("Number of items per page"), per_page: z.number().optional().describe("Number of items per page"),
owned: z.boolean().optional().describe("Filter for projects owned by current user"), search_namespaces: z
membership: z.boolean().optional().describe("Filter for projects where current user is a member"), .boolean()
.optional()
.describe("Needs to be true if search is full path"),
owned: z
.boolean()
.optional()
.describe("Filter for projects owned by current user"),
membership: z
.boolean()
.optional()
.describe("Filter for projects where current user is a member"),
simple: z.boolean().optional().describe("Return only limited fields"), simple: z.boolean().optional().describe("Return only limited fields"),
archived: z.boolean().optional().describe("Filter for archived projects"), archived: z.boolean().optional().describe("Filter for archived projects"),
visibility: z.enum(["public", "internal", "private"]).optional().describe("Filter by project visibility"), visibility: z
order_by: z.enum(["id", "name", "path", "created_at", "updated_at", "last_activity_at"]).optional().describe("Return projects ordered by field"), .enum(["public", "internal", "private"])
sort: z.enum(["asc", "desc"]).optional().describe("Return projects sorted in ascending or descending order"), .optional()
with_issues_enabled: z.boolean().optional().describe("Filter projects with issues feature enabled"), .describe("Filter by project visibility"),
with_merge_requests_enabled: z.boolean().optional().describe("Filter projects with merge requests feature enabled"), order_by: z
min_access_level: z.number().optional().describe("Filter by minimum access level"), .enum([
"id",
"name",
"path",
"created_at",
"updated_at",
"last_activity_at",
])
.optional()
.describe("Return projects ordered by field"),
sort: z
.enum(["asc", "desc"])
.optional()
.describe("Return projects sorted in ascending or descending order"),
with_issues_enabled: z
.boolean()
.optional()
.describe("Filter projects with issues feature enabled"),
with_merge_requests_enabled: z
.boolean()
.optional()
.describe("Filter projects with merge requests feature enabled"),
min_access_level: z
.number()
.optional()
.describe("Filter by minimum access level"),
}); });
// Label operation schemas // Label operation schemas
export const ListLabelsSchema = z.object({ export const ListLabelsSchema = z.object({
project_id: z.string().describe("Project ID or URL-encoded path"), project_id: z.string().describe("Project ID or URL-encoded path"),
with_counts: z.boolean().optional().describe("Whether or not to include issue and merge request counts"), with_counts: z
include_ancestor_groups: z.boolean().optional().describe("Include ancestor groups"), .boolean()
.optional()
.describe("Whether or not to include issue and merge request counts"),
include_ancestor_groups: z
.boolean()
.optional()
.describe("Include ancestor groups"),
search: z.string().optional().describe("Keyword to filter labels by"), search: z.string().optional().describe("Keyword to filter labels by"),
}); });
export const GetLabelSchema = z.object({ export const GetLabelSchema = z.object({
project_id: z.string().describe("Project ID or URL-encoded path"), project_id: z.string().describe("Project ID or URL-encoded path"),
label_id: z.union([z.number(), z.string()]).describe("The ID or title of a project's label"), label_id: z.string().describe("The ID or title of a project's label"),
include_ancestor_groups: z.boolean().optional().describe("Include ancestor groups"), include_ancestor_groups: z
.boolean()
.optional()
.describe("Include ancestor groups"),
}); });
export const CreateLabelSchema = z.object({ export const CreateLabelSchema = z.object({
project_id: z.string().describe("Project ID or URL-encoded path"), project_id: z.string().describe("Project ID or URL-encoded path"),
name: z.string().describe("The name of the label"), name: z.string().describe("The name of the label"),
color: z.string().describe("The color of the label given in 6-digit hex notation with leading '#' sign"), color: z
.string()
.describe(
"The color of the label given in 6-digit hex notation with leading '#' sign"
),
description: z.string().optional().describe("The description of the label"), description: z.string().optional().describe("The description of the label"),
priority: z.number().nullable().optional().describe("The priority of the label"), priority: z
.number()
.nullable()
.optional()
.describe("The priority of the label"),
}); });
export const UpdateLabelSchema = z.object({ export const UpdateLabelSchema = z.object({
project_id: z.string().describe("Project ID or URL-encoded path"), project_id: z.string().describe("Project ID or URL-encoded path"),
label_id: z.union([z.number(), z.string()]).describe("The ID or title of a project's label"), label_id: z.string().describe("The ID or title of a project's label"),
new_name: z.string().optional().describe("The new name of the label"), new_name: z.string().optional().describe("The new name of the label"),
color: z.string().optional().describe("The color of the label given in 6-digit hex notation with leading '#' sign"), color: z
description: z.string().optional().describe("The new description of the label"), .string()
priority: z.number().nullable().optional().describe("The new priority of the label"), .optional()
.describe(
"The color of the label given in 6-digit hex notation with leading '#' sign"
),
description: z
.string()
.optional()
.describe("The new description of the label"),
priority: z
.number()
.nullable()
.optional()
.describe("The new priority of the label"),
}); });
export const DeleteLabelSchema = z.object({ export const DeleteLabelSchema = z.object({
project_id: z.string().describe("Project ID or URL-encoded path"), project_id: z.string().describe("Project ID or URL-encoded path"),
label_id: z.union([z.number(), z.string()]).describe("The ID or title of a project's label"), label_id: z.string().describe("The ID or title of a project's label"),
}); });
// Group projects schema // Group projects schema
export const ListGroupProjectsSchema = z.object({ export const ListGroupProjectsSchema = z.object({
group_id: z.string().describe("Group ID or path"), group_id: z.string().describe("Group ID or path"),
include_subgroups: z.boolean().optional().describe("Include projects from subgroups"), include_subgroups: z
.boolean()
.optional()
.describe("Include projects from subgroups"),
search: z.string().optional().describe("Search term to filter projects"), search: z.string().optional().describe("Search term to filter projects"),
order_by: z.enum(['name', 'path', 'created_at', 'updated_at', 'last_activity_at']).optional().describe("Field to sort by"), order_by: z
sort: z.enum(['asc', 'desc']).optional().describe("Sort direction"), .enum(["name", "path", "created_at", "updated_at", "last_activity_at"])
.optional()
.describe("Field to sort by"),
sort: z.enum(["asc", "desc"]).optional().describe("Sort direction"),
page: z.number().optional().describe("Page number"), page: z.number().optional().describe("Page number"),
per_page: z.number().optional().describe("Number of results per page"), per_page: z.number().optional().describe("Number of results per page"),
archived: z.boolean().optional().describe("Filter for archived projects"), archived: z.boolean().optional().describe("Filter for archived projects"),
visibility: z.enum(["public", "internal", "private"]).optional().describe("Filter by project visibility"), visibility: z
with_issues_enabled: z.boolean().optional().describe("Filter projects with issues feature enabled"), .enum(["public", "internal", "private"])
with_merge_requests_enabled: z.boolean().optional().describe("Filter projects with merge requests feature enabled"), .optional()
min_access_level: z.number().optional().describe("Filter by minimum access level"), .describe("Filter by project visibility"),
with_programming_language: z.string().optional().describe("Filter by programming language"), with_issues_enabled: z
.boolean()
.optional()
.describe("Filter projects with issues feature enabled"),
with_merge_requests_enabled: z
.boolean()
.optional()
.describe("Filter projects with merge requests feature enabled"),
min_access_level: z
.number()
.optional()
.describe("Filter by minimum access level"),
with_programming_language: z
.string()
.optional()
.describe("Filter by programming language"),
starred: z.boolean().optional().describe("Filter by starred projects"), starred: z.boolean().optional().describe("Filter by starred projects"),
statistics: z.boolean().optional().describe("Include project statistics"), statistics: z.boolean().optional().describe("Include project statistics"),
with_custom_attributes: z.boolean().optional().describe("Include custom attributes"), with_custom_attributes: z
with_security_reports: z.boolean().optional().describe("Include security reports") .boolean()
.optional()
.describe("Include custom attributes"),
with_security_reports: z
.boolean()
.optional()
.describe("Include security reports"),
}); });
// Add wiki operation schemas // Add wiki operation schemas
@ -770,14 +1124,20 @@ export const CreateWikiPageSchema = z.object({
project_id: z.string().describe("Project ID or URL-encoded path"), project_id: z.string().describe("Project ID or URL-encoded path"),
title: z.string().describe("Title of the wiki page"), title: z.string().describe("Title of the wiki page"),
content: z.string().describe("Content of the wiki page"), content: z.string().describe("Content of the wiki page"),
format: z.string().optional().describe("Content format, e.g., markdown, rdoc"), format: z
.string()
.optional()
.describe("Content format, e.g., markdown, rdoc"),
}); });
export const UpdateWikiPageSchema = z.object({ export const UpdateWikiPageSchema = z.object({
project_id: z.string().describe("Project ID or URL-encoded path"), project_id: z.string().describe("Project ID or URL-encoded path"),
slug: z.string().describe("URL-encoded slug of the wiki page"), slug: z.string().describe("URL-encoded slug of the wiki page"),
title: z.string().optional().describe("New title of the wiki page"), title: z.string().optional().describe("New title of the wiki page"),
content: z.string().optional().describe("New content of the wiki page"), content: z.string().optional().describe("New content of the wiki page"),
format: z.string().optional().describe("Content format, e.g., markdown, rdoc"), format: z
.string()
.optional()
.describe("Content format, e.g., markdown, rdoc"),
}); });
export const DeleteWikiPageSchema = z.object({ export const DeleteWikiPageSchema = z.object({
project_id: z.string().describe("Project ID or URL-encoded path"), project_id: z.string().describe("Project ID or URL-encoded path"),
@ -794,31 +1154,72 @@ export const GitLabWikiPageSchema = z.object({
updated_at: z.string().optional(), updated_at: z.string().optional(),
}); });
// Merge Request Thread position schema - used for diff notes
export const MergeRequestThreadPositionSchema = z.object({
base_sha: z.string().describe("Base commit SHA in the source branch"),
head_sha: z.string().describe("SHA referencing HEAD of the source branch"),
start_sha: z.string().describe("SHA referencing the start commit of the source branch"),
position_type: z.enum(["text", "image", "file"]).describe("Type of position reference"),
new_path: z.string().optional().describe("File path after change"),
old_path: z.string().optional().describe("File path before change"),
new_line: z.number().nullable().optional().describe("Line number after change"),
old_line: z.number().nullable().optional().describe("Line number before change"),
width: z.number().optional().describe("Width of the image (for image diffs)"),
height: z.number().optional().describe("Height of the image (for image diffs)"),
x: z.number().optional().describe("X coordinate on the image (for image diffs)"),
y: z.number().optional().describe("Y coordinate on the image (for image diffs)"),
});
// Schema for creating a new merge request thread
export const CreateMergeRequestThreadSchema = ProjectParamsSchema.extend({
merge_request_iid: z.number().describe("The IID of a merge request"),
body: z.string().describe("The content of the thread"),
position: MergeRequestThreadPositionSchema.optional().describe("Position when creating a diff note"),
created_at: z.string().optional().describe("Date the thread was created at (ISO 8601 format)"),
});
// Export types // Export types
export type GitLabAuthor = z.infer<typeof GitLabAuthorSchema>; export type GitLabAuthor = z.infer<typeof GitLabAuthorSchema>;
export type GitLabFork = z.infer<typeof GitLabForkSchema>; export type GitLabFork = z.infer<typeof GitLabForkSchema>;
export type GitLabIssue = z.infer<typeof GitLabIssueSchema>; export type GitLabIssue = z.infer<typeof GitLabIssueSchema>;
export type GitLabIssueWithLinkDetails = z.infer<typeof GitLabIssueWithLinkDetailsSchema>; export type GitLabIssueWithLinkDetails = z.infer<
typeof GitLabIssueWithLinkDetailsSchema
>;
export type GitLabMergeRequest = z.infer<typeof GitLabMergeRequestSchema>; export type GitLabMergeRequest = z.infer<typeof GitLabMergeRequestSchema>;
export type GitLabRepository = z.infer<typeof GitLabRepositorySchema>; export type GitLabRepository = z.infer<typeof GitLabRepositorySchema>;
export type GitLabFileContent = z.infer<typeof GitLabFileContentSchema>; export type GitLabFileContent = z.infer<typeof GitLabFileContentSchema>;
export type GitLabDirectoryContent = z.infer<typeof GitLabDirectoryContentSchema>; export type GitLabDirectoryContent = z.infer<
typeof GitLabDirectoryContentSchema
>;
export type GitLabContent = z.infer<typeof GitLabContentSchema>; export type GitLabContent = z.infer<typeof GitLabContentSchema>;
export type FileOperation = z.infer<typeof FileOperationSchema>; export type FileOperation = z.infer<typeof FileOperationSchema>;
export type GitLabTree = z.infer<typeof GitLabTreeSchema>; export type GitLabTree = z.infer<typeof GitLabTreeSchema>;
export type GitLabCommit = z.infer<typeof GitLabCommitSchema>; export type GitLabCommit = z.infer<typeof GitLabCommitSchema>;
export type GitLabReference = z.infer<typeof GitLabReferenceSchema>; export type GitLabReference = z.infer<typeof GitLabReferenceSchema>;
export type CreateRepositoryOptions = z.infer<typeof CreateRepositoryOptionsSchema>; export type CreateRepositoryOptions = z.infer<
typeof CreateRepositoryOptionsSchema
>;
export type CreateIssueOptions = z.infer<typeof CreateIssueOptionsSchema>; export type CreateIssueOptions = z.infer<typeof CreateIssueOptionsSchema>;
export type CreateMergeRequestOptions = z.infer<typeof CreateMergeRequestOptionsSchema>; export type CreateMergeRequestOptions = z.infer<
typeof CreateMergeRequestOptionsSchema
>;
export type CreateBranchOptions = z.infer<typeof CreateBranchOptionsSchema>; export type CreateBranchOptions = z.infer<typeof CreateBranchOptionsSchema>;
export type GitLabCreateUpdateFileResponse = z.infer<typeof GitLabCreateUpdateFileResponseSchema>; export type GitLabCreateUpdateFileResponse = z.infer<
typeof GitLabCreateUpdateFileResponseSchema
>;
export type GitLabSearchResponse = z.infer<typeof GitLabSearchResponseSchema>; export type GitLabSearchResponse = z.infer<typeof GitLabSearchResponseSchema>;
export type GitLabMergeRequestDiff = z.infer<typeof GitLabMergeRequestDiffSchema>; export type GitLabMergeRequestDiff = z.infer<
typeof GitLabMergeRequestDiffSchema
>;
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 UpdateIssueNoteOptions = z.infer<typeof UpdateIssueNoteSchema>;
export type CreateIssueNoteOptions = z.infer<typeof CreateIssueNoteSchema>;
export type GitLabNamespace = z.infer<typeof GitLabNamespaceSchema>; export type GitLabNamespace = z.infer<typeof GitLabNamespaceSchema>;
export type GitLabNamespaceExistsResponse = z.infer<typeof GitLabNamespaceExistsResponseSchema>; export type GitLabNamespaceExistsResponse = z.infer<
typeof GitLabNamespaceExistsResponseSchema
>;
export type GitLabProject = z.infer<typeof GitLabProjectSchema>; export type GitLabProject = z.infer<typeof GitLabProjectSchema>;
export type GitLabLabel = z.infer<typeof GitLabLabelSchema>; export type GitLabLabel = z.infer<typeof GitLabLabelSchema>;
export type ListWikiPagesOptions = z.infer<typeof ListWikiPagesSchema>; export type ListWikiPagesOptions = z.infer<typeof ListWikiPagesSchema>;
@ -827,3 +1228,13 @@ export type CreateWikiPageOptions = z.infer<typeof CreateWikiPageSchema>;
export type UpdateWikiPageOptions = z.infer<typeof UpdateWikiPageSchema>; export type UpdateWikiPageOptions = z.infer<typeof UpdateWikiPageSchema>;
export type DeleteWikiPageOptions = z.infer<typeof DeleteWikiPageSchema>; export type DeleteWikiPageOptions = z.infer<typeof DeleteWikiPageSchema>;
export type GitLabWikiPage = z.infer<typeof GitLabWikiPageSchema>; export type GitLabWikiPage = z.infer<typeof GitLabWikiPageSchema>;
export type GitLabTreeItem = z.infer<typeof GitLabTreeItemSchema>;
export type GetRepositoryTreeOptions = z.infer<typeof GetRepositoryTreeSchema>;
export type MergeRequestThreadPosition = z.infer<typeof MergeRequestThreadPositionSchema>;
export type CreateMergeRequestThreadOptions = z.infer<typeof CreateMergeRequestThreadSchema>;
export type CreateMergeRequestNoteOptions = z.infer<typeof CreateMergeRequestNoteSchema>;
export type GitLabPipelineJob = z.infer<typeof GitLabPipelineJobSchema>;
export type GitLabPipeline = z.infer<typeof GitLabPipelineSchema>;
export type ListPipelinesOptions = z.infer<typeof ListPipelinesSchema>;
export type GetPipelineOptions = z.infer<typeof GetPipelineSchema>;
export type ListPipelineJobsOptions = z.infer<typeof ListPipelineJobsSchema>;