feat(gitlab): add repository, code-review, and CI job tools + validation fixes (#5205)

* feat(gitlab): add repository, code-review, and CI job tools + validation fixes

Expand the GitLab integration with 12 new tools (all host-aware via
getGitLabApiBase, wired through types/index/registry/block):
- Repository: list_repository_tree, get_file, create_file, update_file,
  create_branch, list_branches, list_commits
- Code review: get_merge_request_changes, approve_merge_request
- CI jobs: list_pipeline_jobs, get_job_log, play_job

Validation fixes from /validate-integration:
- Correct the block inputs key (credential -> accessToken) so it matches the
  subBlock id and the params the block reads
- Trim projectId before encoding in all tool request URLs (input hygiene)

/validate-connector and /validate-trigger passed clean against the GitLab REST
API v4 docs — no changes required.

* fix(gitlab): address review feedback + regen docs

- get_merge_request_changes: use the /diffs endpoint (/changes was removed in
  GitLab 18.0); return the diff array + count (drops the MR envelope that /diffs
  no longer provides), fetch max page size in a single call
- create_file/update_file: send explicit `encoding: 'text'` for clarity
- Remove `// =====` separator comments from types.ts (repo convention)
- Regenerate GitLab integration docs + catalog for the 12 new tools
This commit is contained in:
Waleed
2026-06-24 18:05:40 -07:00
committed by GitHub
parent c3a09694ab
commit ae4bc05e60
36 changed files with 2526 additions and 74 deletions
@@ -41,6 +41,7 @@ List GitLab projects accessible to the authenticated user
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. |
| `owned` | boolean | No | Limit to projects owned by the current user |
| `membership` | boolean | No | Limit to projects the current user is a member of |
| `search` | string | No | Search projects by name |
@@ -65,6 +66,7 @@ Get details of a specific GitLab project
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. |
| `projectId` | string | Yes | Project ID or URL-encoded path \(e.g., "namespace/project"\) |
#### Output
@@ -81,6 +83,7 @@ List issues in a GitLab project
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. |
| `projectId` | string | Yes | Project ID or URL-encoded path |
| `state` | string | No | Filter by state \(opened, closed, all\) |
| `labels` | string | No | Comma-separated list of label names |
@@ -107,6 +110,7 @@ Get details of a specific GitLab issue
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. |
| `projectId` | string | Yes | Project ID or URL-encoded path |
| `issueIid` | number | Yes | Issue number within the project \(the # shown in GitLab UI\) |
@@ -124,6 +128,7 @@ Create a new issue in a GitLab project
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. |
| `projectId` | string | Yes | Project ID or URL-encoded path |
| `title` | string | Yes | Issue title |
| `description` | string | No | Issue description \(Markdown supported\) |
@@ -147,6 +152,7 @@ Update an existing issue in a GitLab project
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. |
| `projectId` | string | Yes | Project ID or URL-encoded path |
| `issueIid` | number | Yes | Issue internal ID \(IID\) |
| `title` | string | No | New issue title |
@@ -172,6 +178,7 @@ Delete an issue from a GitLab project
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. |
| `projectId` | string | Yes | Project ID or URL-encoded path |
| `issueIid` | number | Yes | Issue internal ID \(IID\) |
@@ -189,6 +196,7 @@ Add a comment to a GitLab issue
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. |
| `projectId` | string | Yes | Project ID or URL-encoded path |
| `issueIid` | number | Yes | Issue internal ID \(IID\) |
| `body` | string | Yes | Comment body \(Markdown supported\) |
@@ -207,6 +215,7 @@ List merge requests in a GitLab project
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. |
| `projectId` | string | Yes | Project ID or URL-encoded path |
| `state` | string | No | Filter by state \(opened, closed, merged, all\) |
| `labels` | string | No | Comma-separated list of label names |
@@ -232,6 +241,7 @@ Get details of a specific GitLab merge request
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. |
| `projectId` | string | Yes | Project ID or URL-encoded path |
| `mergeRequestIid` | number | Yes | Merge request internal ID \(IID\) |
@@ -249,6 +259,7 @@ Create a new merge request in a GitLab project
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. |
| `projectId` | string | Yes | Project ID or URL-encoded path |
| `sourceBranch` | string | Yes | Source branch name |
| `targetBranch` | string | Yes | Target branch name |
@@ -275,6 +286,7 @@ Update an existing merge request in a GitLab project
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. |
| `projectId` | string | Yes | Project ID or URL-encoded path |
| `mergeRequestIid` | number | Yes | Merge request internal ID \(IID\) |
| `title` | string | No | New merge request title |
@@ -302,6 +314,7 @@ Merge a merge request in a GitLab project
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. |
| `projectId` | string | Yes | Project ID or URL-encoded path |
| `mergeRequestIid` | number | Yes | Merge request internal ID \(IID\) |
| `mergeCommitMessage` | string | No | Custom merge commit message |
@@ -324,6 +337,7 @@ Add a comment to a GitLab merge request
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. |
| `projectId` | string | Yes | Project ID or URL-encoded path |
| `mergeRequestIid` | number | Yes | Merge request internal ID \(IID\) |
| `body` | string | Yes | Comment body \(Markdown supported\) |
@@ -342,6 +356,7 @@ List pipelines in a GitLab project
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. |
| `projectId` | string | Yes | Project ID or URL-encoded path |
| `ref` | string | No | Filter by ref \(branch or tag\) |
| `status` | string | No | Filter by status \(created, waiting_for_resource, preparing, pending, running, success, failed, canceled, skipped, manual, scheduled\) |
@@ -365,6 +380,7 @@ Get details of a specific GitLab pipeline
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. |
| `projectId` | string | Yes | Project ID or URL-encoded path |
| `pipelineId` | number | Yes | Pipeline ID |
@@ -382,6 +398,7 @@ Trigger a new pipeline in a GitLab project
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. |
| `projectId` | string | Yes | Project ID or URL-encoded path |
| `ref` | string | Yes | Branch or tag to run the pipeline on |
| `variables` | array | No | Array of variables for the pipeline \(each with key, value, and optional variable_type\) |
@@ -400,6 +417,7 @@ Retry a failed GitLab pipeline
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. |
| `projectId` | string | Yes | Project ID or URL-encoded path |
| `pipelineId` | number | Yes | Pipeline ID |
@@ -417,6 +435,7 @@ Cancel a running GitLab pipeline
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. |
| `projectId` | string | Yes | Project ID or URL-encoded path |
| `pipelineId` | number | Yes | Pipeline ID |
@@ -426,4 +445,449 @@ Cancel a running GitLab pipeline
| --------- | ---- | ----------- |
| `pipeline` | object | The cancelled GitLab pipeline |
### `gitlab_list_repository_tree`
List files and directories in a GitLab project repository
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. |
| `projectId` | string | Yes | Project ID or URL-encoded path |
| `path` | string | No | Path inside the repository to list |
| `ref` | string | No | Branch, tag, or commit SHA to list from |
| `recursive` | boolean | No | Whether to list files recursively |
| `perPage` | number | No | Number of results per page \(default 20, max 100\) |
| `page` | number | No | Page number for pagination |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `tree` | array | List of repository tree entries |
| `total` | number | Total number of tree entries |
### `gitlab_get_file`
Get the contents of a file from a GitLab project repository
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. |
| `projectId` | string | Yes | Project ID or URL-encoded path |
| `filePath` | string | Yes | Path to the file in the repository |
| `ref` | string | Yes | Branch, tag, or commit SHA |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `filePath` | string | The file path |
| `fileName` | string | The file name |
| `size` | number | The file size in bytes |
| `ref` | string | The branch, tag, or commit SHA |
| `blobId` | string | The blob ID |
| `lastCommitId` | string | The last commit ID that modified the file |
| `content` | string | The decoded file content |
### `gitlab_create_file`
Create a new file in a GitLab project repository
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. |
| `projectId` | string | Yes | Project ID or URL-encoded path |
| `filePath` | string | Yes | Path to the file in the repository |
| `branch` | string | Yes | Branch to commit the new file to |
| `content` | string | Yes | File content |
| `commitMessage` | string | Yes | Commit message |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `filePath` | string | The created file path |
| `branch` | string | The branch the file was committed to |
### `gitlab_update_file`
Update an existing file in a GitLab project repository
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. |
| `projectId` | string | Yes | Project ID or URL-encoded path |
| `filePath` | string | Yes | Path to the file in the repository |
| `branch` | string | Yes | Branch to commit the update to |
| `content` | string | Yes | New file content |
| `commitMessage` | string | Yes | Commit message |
| `lastCommitId` | string | No | Last known commit ID for the file \(optimistic locking\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `filePath` | string | The updated file path |
| `branch` | string | The branch the update was committed to |
### `gitlab_create_branch`
Create a new branch in a GitLab project repository
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. |
| `projectId` | string | Yes | Project ID or URL-encoded path |
| `branch` | string | Yes | Name of the new branch |
| `ref` | string | Yes | Source branch/tag/SHA |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `name` | string | The created branch name |
| `webUrl` | string | The web URL of the branch |
| `protected` | boolean | Whether the branch is protected |
| `commit` | object | The commit the branch points to |
### `gitlab_list_branches`
List branches in a GitLab project repository
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. |
| `projectId` | string | Yes | Project ID or URL-encoded path |
| `search` | string | No | Filter branches by name |
| `perPage` | number | No | Number of results per page \(default 20, max 100\) |
| `page` | number | No | Page number for pagination |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `branches` | array | List of branches |
| `total` | number | Total number of branches |
### `gitlab_list_commits`
List commits in a GitLab project repository
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. |
| `projectId` | string | Yes | Project ID or URL-encoded path |
| `refName` | string | No | Branch, tag, or revision range to list commits from |
| `since` | string | No | Only commits after this ISO 8601 date |
| `until` | string | No | Only commits before this ISO 8601 date |
| `path` | string | No | Only commits affecting this file path |
| `author` | string | No | Filter commits by author |
| `perPage` | number | No | Number of results per page \(default 20, max 100\) |
| `page` | number | No | Page number for pagination |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `commits` | array | List of commits |
| `total` | number | Total number of commits |
### `gitlab_get_merge_request_changes`
Get the file changes (diffs) of a GitLab merge request
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. |
| `projectId` | string | Yes | Project ID or URL-encoded path |
| `mergeRequestIid` | number | Yes | Merge request internal ID \(IID\) |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `mergeRequestIid` | number | The merge request internal ID \(IID\) |
| `changes` | array | List of file changes \(diffs\) |
| `changesCount` | number | Number of changed files returned |
### `gitlab_approve_merge_request`
Approve a GitLab merge request
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. |
| `projectId` | string | Yes | Project ID or URL-encoded path |
| `mergeRequestIid` | number | Yes | Merge request internal ID \(IID\) |
| `sha` | string | No | HEAD SHA of the merge request to approve |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `approvalsRequired` | number | Number of approvals required |
| `approvalsLeft` | number | Number of approvals still needed |
| `approvedBy` | array | List of approvers |
### `gitlab_list_pipeline_jobs`
List jobs for a GitLab pipeline
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. |
| `projectId` | string | Yes | Project ID or URL-encoded path |
| `pipelineId` | number | Yes | Pipeline ID |
| `scope` | string | No | Filter jobs by scope \(e.g. created, running, success, failed\) |
| `includeRetried` | boolean | No | Whether to include retried jobs |
| `perPage` | number | No | Number of results per page \(default 20, max 100\) |
| `page` | number | No | Page number for pagination |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `jobs` | array | List of pipeline jobs |
| `total` | number | Total number of jobs |
### `gitlab_get_job_log`
Get the log (trace) of a GitLab job
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. |
| `projectId` | string | Yes | Project ID or URL-encoded path |
| `jobId` | number | Yes | Job ID |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `log` | string | The job log \(trace\) output |
### `gitlab_play_job`
Trigger (play) a manual GitLab job
#### Input
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `host` | string | No | Self-managed GitLab host \(e.g. gitlab.example.com\). Defaults to gitlab.com. |
| `projectId` | string | Yes | Project ID or URL-encoded path |
| `jobId` | number | Yes | Job ID |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `id` | number | The job ID |
| `name` | string | The job name |
| `status` | string | The job status |
| `webUrl` | string | The web URL of the job |
## Triggers
A **Trigger** is a block that starts a workflow when an event happens in this service.
### GitLab Comment
Trigger workflow when a comment is added on a commit, merge request, or issue
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `accessToken` | string | Yes | Used to create the webhook in your project. Requires the Maintainer or Owner role. |
| `projectId` | string | Yes | The GitLab project to register the webhook on. |
| `host` | string | No | Self-managed GitLab host. Leave blank for gitlab.com. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `object_kind` | string | Event kind \(note\) |
| `event_type` | string | GitLab event type from the X-Gitlab-Event header |
| `object_attributes` | object | object_attributes output from the tool |
| ↳ `id` | number | Comment ID |
| ↳ `note` | string | Comment body |
| ↳ `noteable_type` | string | What the comment is on \(Commit, MergeRequest, Issue, Snippet\) |
| ↳ `action` | string | Action \(create, update\) |
| ↳ `url` | string | Comment URL |
---
### GitLab Event
Trigger workflow from any GitLab webhook event
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `accessToken` | string | Yes | Used to create the webhook in your project. Requires the Maintainer or Owner role. |
| `projectId` | string | Yes | The GitLab project to register the webhook on. |
| `host` | string | No | Self-managed GitLab host. Leave blank for gitlab.com. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `object_kind` | string | Event kind \(push, merge_request, issue, etc.\) |
| `event_type` | string | GitLab event type from the X-Gitlab-Event header |
| `user` | json | Actor that triggered the event \(when present\) |
| `object_attributes` | json | Event-specific attributes \(varies by object_kind\) |
---
### GitLab Issue
Trigger workflow when an issue is opened, updated, or closed in GitLab
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `accessToken` | string | Yes | Used to create the webhook in your project. Requires the Maintainer or Owner role. |
| `projectId` | string | Yes | The GitLab project to register the webhook on. |
| `host` | string | No | Self-managed GitLab host. Leave blank for gitlab.com. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `object_kind` | string | Event kind \(issue\) |
| `event_type` | string | GitLab event type from the X-Gitlab-Event header |
| `object_attributes` | object | object_attributes output from the tool |
| ↳ `id` | number | Global issue ID |
| ↳ `iid` | number | Project-scoped issue number |
| ↳ `title` | string | Issue title |
| ↳ `state` | string | State \(opened, closed\) |
| ↳ `action` | string | Action \(open, close, reopen, update\) |
| ↳ `description` | string | Issue description |
| ↳ `confidential` | boolean | Whether the issue is confidential |
| ↳ `url` | string | Issue URL |
---
### GitLab Merge Request
Trigger workflow when a merge request is opened, updated, or merged in GitLab
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `accessToken` | string | Yes | Used to create the webhook in your project. Requires the Maintainer or Owner role. |
| `projectId` | string | Yes | The GitLab project to register the webhook on. |
| `host` | string | No | Self-managed GitLab host. Leave blank for gitlab.com. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `object_kind` | string | Event kind \(merge_request\) |
| `event_type` | string | GitLab event type from the X-Gitlab-Event header |
| `object_attributes` | object | object_attributes output from the tool |
| ↳ `id` | number | Global merge request ID |
| ↳ `iid` | number | Project-scoped merge request number |
| ↳ `title` | string | Merge request title |
| ↳ `state` | string | State \(opened, closed, merged, locked\) |
| ↳ `action` | string | Action \(open, close, reopen, update, merge, etc.\) |
| ↳ `source_branch` | string | Source branch |
| ↳ `target_branch` | string | Target branch |
| ↳ `merge_status` | string | Merge status |
| ↳ `draft` | boolean | Whether the merge request is a draft |
| ↳ `url` | string | Merge request URL |
---
### GitLab Pipeline
Trigger workflow when a pipeline status changes in GitLab
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `accessToken` | string | Yes | Used to create the webhook in your project. Requires the Maintainer or Owner role. |
| `projectId` | string | Yes | The GitLab project to register the webhook on. |
| `host` | string | No | Self-managed GitLab host. Leave blank for gitlab.com. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `object_kind` | string | Event kind \(pipeline\) |
| `event_type` | string | GitLab event type from the X-Gitlab-Event header |
| `object_attributes` | object | object_attributes output from the tool |
| ↳ `id` | number | Pipeline ID |
| ↳ `status` | string | Pipeline status \(success, failed, running, etc.\) |
| ↳ `detailed_status` | string | Detailed pipeline status |
| ↳ `ref` | string | Ref the pipeline ran on |
| ↳ `sha` | string | Commit SHA |
| ↳ `source` | string | Pipeline source \(push, web, schedule, etc.\) |
| ↳ `duration` | number | Pipeline duration in seconds |
| ↳ `url` | string | Pipeline URL |
---
### GitLab Push
Trigger workflow when commits are pushed to a GitLab project
#### Configuration
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `accessToken` | string | Yes | Used to create the webhook in your project. Requires the Maintainer or Owner role. |
| `projectId` | string | Yes | The GitLab project to register the webhook on. |
| `host` | string | No | Self-managed GitLab host. Leave blank for gitlab.com. |
#### Output
| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `object_kind` | string | Event kind \(push\) |
| `event_type` | string | GitLab event type from the X-Gitlab-Event header |
| `ref` | string | Git ref that was pushed \(e.g. refs/heads/main\) |
| `branch` | string | Branch name derived from ref |
| `before` | string | SHA before the push |
| `after` | string | SHA after the push |
| `checkout_sha` | string | SHA of the most recent commit |
| `user_username` | string | Username of the pusher |
| `user_name` | string | Display name of the pusher |
| `user_email` | string | Email of the pusher |
| `total_commits_count` | number | Number of commits in the push |
| `commits` | json | Array of commit objects included in this push |
+344 -3
View File
@@ -46,6 +46,21 @@ export const GitLabBlock: BlockConfig<GitLabResponse> = {
{ label: 'Create Pipeline', id: 'gitlab_create_pipeline' },
{ label: 'Retry Pipeline', id: 'gitlab_retry_pipeline' },
{ label: 'Cancel Pipeline', id: 'gitlab_cancel_pipeline' },
// Repository Operations
{ label: 'List Repository Tree', id: 'gitlab_list_repository_tree' },
{ label: 'Get File', id: 'gitlab_get_file' },
{ label: 'Create File', id: 'gitlab_create_file' },
{ label: 'Update File', id: 'gitlab_update_file' },
{ label: 'List Commits', id: 'gitlab_list_commits' },
{ label: 'List Branches', id: 'gitlab_list_branches' },
{ label: 'Create Branch', id: 'gitlab_create_branch' },
// Additional Merge Request Operations
{ label: 'Get MR Changes', id: 'gitlab_get_merge_request_changes' },
{ label: 'Approve Merge Request', id: 'gitlab_approve_merge_request' },
// Job Operations
{ label: 'List Pipeline Jobs', id: 'gitlab_list_pipeline_jobs' },
{ label: 'Get Job Log', id: 'gitlab_get_job_log' },
{ label: 'Play Job', id: 'gitlab_play_job' },
],
value: () => 'gitlab_list_projects',
},
@@ -94,6 +109,18 @@ export const GitLabBlock: BlockConfig<GitLabResponse> = {
'gitlab_create_pipeline',
'gitlab_retry_pipeline',
'gitlab_cancel_pipeline',
'gitlab_list_repository_tree',
'gitlab_get_file',
'gitlab_create_file',
'gitlab_update_file',
'gitlab_list_commits',
'gitlab_list_branches',
'gitlab_create_branch',
'gitlab_get_merge_request_changes',
'gitlab_approve_merge_request',
'gitlab_list_pipeline_jobs',
'gitlab_get_job_log',
'gitlab_play_job',
],
},
},
@@ -128,6 +155,8 @@ export const GitLabBlock: BlockConfig<GitLabResponse> = {
'gitlab_update_merge_request',
'gitlab_merge_merge_request',
'gitlab_create_merge_request_note',
'gitlab_get_merge_request_changes',
'gitlab_approve_merge_request',
],
},
},
@@ -140,7 +169,12 @@ export const GitLabBlock: BlockConfig<GitLabResponse> = {
required: true,
condition: {
field: 'operation',
value: ['gitlab_get_pipeline', 'gitlab_retry_pipeline', 'gitlab_cancel_pipeline'],
value: [
'gitlab_get_pipeline',
'gitlab_retry_pipeline',
'gitlab_cancel_pipeline',
'gitlab_list_pipeline_jobs',
],
},
},
// Title (for issue/MR creation)
@@ -247,7 +281,135 @@ Return ONLY the comment text - no explanations, no extra formatting.`,
required: true,
condition: {
field: 'operation',
value: ['gitlab_create_pipeline'],
value: ['gitlab_create_pipeline', 'gitlab_get_file', 'gitlab_create_branch'],
},
},
// File Path
{
id: 'filePath',
title: 'File Path',
type: 'short-input',
placeholder: 'Path to file (e.g., src/index.ts)',
required: true,
condition: {
field: 'operation',
value: ['gitlab_get_file', 'gitlab_create_file', 'gitlab_update_file'],
},
},
// Branch
{
id: 'branch',
title: 'Branch',
type: 'short-input',
placeholder: 'Branch name',
required: true,
condition: {
field: 'operation',
value: ['gitlab_create_file', 'gitlab_update_file', 'gitlab_create_branch'],
},
},
// File Content
{
id: 'content',
title: 'File Content',
type: 'long-input',
placeholder: 'File content',
required: true,
condition: {
field: 'operation',
value: ['gitlab_create_file', 'gitlab_update_file'],
},
},
// Commit Message
{
id: 'commitMessage',
title: 'Commit Message',
type: 'short-input',
placeholder: 'Commit message',
required: true,
condition: {
field: 'operation',
value: ['gitlab_create_file', 'gitlab_update_file'],
},
},
// Job ID
{
id: 'jobId',
title: 'Job ID',
type: 'short-input',
placeholder: 'Enter job ID',
required: true,
condition: {
field: 'operation',
value: ['gitlab_get_job_log', 'gitlab_play_job'],
},
},
// Subdirectory path (for repository tree)
{
id: 'path',
title: 'Path',
type: 'short-input',
placeholder: 'Subdirectory path (optional)',
mode: 'advanced',
condition: {
field: 'operation',
value: ['gitlab_list_repository_tree'],
},
},
// Recursive tree listing
{
id: 'recursive',
title: 'Recursive',
type: 'switch',
mode: 'advanced',
condition: {
field: 'operation',
value: ['gitlab_list_repository_tree'],
},
},
// Ref name filter (for list commits)
{
id: 'refName',
title: 'Ref (branch/tag)',
type: 'short-input',
placeholder: 'Branch or tag (optional)',
mode: 'advanced',
condition: {
field: 'operation',
value: ['gitlab_list_commits'],
},
},
// Job scope filter (for list pipeline jobs)
{
id: 'scope',
title: 'Job Scope',
type: 'dropdown',
options: [
{ label: 'All', id: '' },
{ label: 'Failed', id: 'failed' },
{ label: 'Success', id: 'success' },
{ label: 'Running', id: 'running' },
{ label: 'Pending', id: 'pending' },
{ label: 'Canceled', id: 'canceled' },
{ label: 'Manual', id: 'manual' },
],
value: () => '',
mode: 'advanced',
condition: {
field: 'operation',
value: ['gitlab_list_pipeline_jobs'],
},
},
// Commit SHA (for approve merge request)
{
id: 'sha',
title: 'Commit SHA',
type: 'short-input',
placeholder: 'Optional HEAD SHA to approve',
mode: 'advanced',
condition: {
field: 'operation',
value: ['gitlab_approve_merge_request'],
},
},
// Labels
@@ -427,6 +589,10 @@ Return ONLY the commit message - no explanations, no extra text.`,
'gitlab_list_issues',
'gitlab_list_merge_requests',
'gitlab_list_pipelines',
'gitlab_list_repository_tree',
'gitlab_list_branches',
'gitlab_list_commits',
'gitlab_list_pipeline_jobs',
],
},
},
@@ -444,6 +610,10 @@ Return ONLY the commit message - no explanations, no extra text.`,
'gitlab_list_issues',
'gitlab_list_merge_requests',
'gitlab_list_pipelines',
'gitlab_list_repository_tree',
'gitlab_list_branches',
'gitlab_list_commits',
'gitlab_list_pipeline_jobs',
],
},
},
@@ -475,6 +645,18 @@ Return ONLY the commit message - no explanations, no extra text.`,
'gitlab_create_pipeline',
'gitlab_retry_pipeline',
'gitlab_cancel_pipeline',
'gitlab_list_repository_tree',
'gitlab_get_file',
'gitlab_create_file',
'gitlab_update_file',
'gitlab_create_branch',
'gitlab_list_branches',
'gitlab_list_commits',
'gitlab_get_merge_request_changes',
'gitlab_approve_merge_request',
'gitlab_list_pipeline_jobs',
'gitlab_get_job_log',
'gitlab_play_job',
],
config: {
tool: (params) => {
@@ -710,6 +892,140 @@ Return ONLY the commit message - no explanations, no extra text.`,
pipelineId: Number(params.pipelineId),
}
case 'gitlab_list_repository_tree':
if (!params.projectId?.trim()) {
throw new Error('Project ID is required.')
}
return {
...baseParams,
projectId: params.projectId.trim(),
path: params.path?.trim() || undefined,
recursive: params.recursive || undefined,
perPage: params.perPage ? Number(params.perPage) : undefined,
page: params.page ? Number(params.page) : undefined,
}
case 'gitlab_get_file':
if (!params.projectId?.trim() || !params.filePath?.trim() || !params.ref?.trim()) {
throw new Error('Project ID, file path, and ref are required.')
}
return {
...baseParams,
projectId: params.projectId.trim(),
filePath: params.filePath.trim(),
ref: params.ref.trim(),
}
case 'gitlab_create_file':
case 'gitlab_update_file':
if (
!params.projectId?.trim() ||
!params.filePath?.trim() ||
!params.branch?.trim() ||
!params.content ||
!params.commitMessage?.trim()
) {
throw new Error(
'Project ID, file path, branch, content, and commit message are required.'
)
}
return {
...baseParams,
projectId: params.projectId.trim(),
filePath: params.filePath.trim(),
branch: params.branch.trim(),
content: params.content,
commitMessage: params.commitMessage.trim(),
}
case 'gitlab_create_branch':
if (!params.projectId?.trim() || !params.branch?.trim() || !params.ref?.trim()) {
throw new Error('Project ID, branch name, and source ref are required.')
}
return {
...baseParams,
projectId: params.projectId.trim(),
branch: params.branch.trim(),
ref: params.ref.trim(),
}
case 'gitlab_list_branches':
if (!params.projectId?.trim()) {
throw new Error('Project ID is required.')
}
return {
...baseParams,
projectId: params.projectId.trim(),
perPage: params.perPage ? Number(params.perPage) : undefined,
page: params.page ? Number(params.page) : undefined,
}
case 'gitlab_list_commits':
if (!params.projectId?.trim()) {
throw new Error('Project ID is required.')
}
return {
...baseParams,
projectId: params.projectId.trim(),
refName: params.refName?.trim() || undefined,
perPage: params.perPage ? Number(params.perPage) : undefined,
page: params.page ? Number(params.page) : undefined,
}
case 'gitlab_get_merge_request_changes':
if (!params.projectId?.trim() || !params.mergeRequestIid) {
throw new Error('Project ID and Merge Request IID are required.')
}
return {
...baseParams,
projectId: params.projectId.trim(),
mergeRequestIid: Number(params.mergeRequestIid),
}
case 'gitlab_approve_merge_request':
if (!params.projectId?.trim() || !params.mergeRequestIid) {
throw new Error('Project ID and Merge Request IID are required.')
}
return {
...baseParams,
projectId: params.projectId.trim(),
mergeRequestIid: Number(params.mergeRequestIid),
sha: params.sha?.trim() || undefined,
}
case 'gitlab_list_pipeline_jobs':
if (!params.projectId?.trim() || !params.pipelineId) {
throw new Error('Project ID and Pipeline ID are required.')
}
return {
...baseParams,
projectId: params.projectId.trim(),
pipelineId: Number(params.pipelineId),
scope: params.scope || undefined,
perPage: params.perPage ? Number(params.perPage) : undefined,
page: params.page ? Number(params.page) : undefined,
}
case 'gitlab_get_job_log':
if (!params.projectId?.trim() || !params.jobId) {
throw new Error('Project ID and Job ID are required.')
}
return {
...baseParams,
projectId: params.projectId.trim(),
jobId: Number(params.jobId),
}
case 'gitlab_play_job':
if (!params.projectId?.trim() || !params.jobId) {
throw new Error('Project ID and Job ID are required.')
}
return {
...baseParams,
projectId: params.projectId.trim(),
jobId: Number(params.jobId),
}
default:
return baseParams
}
@@ -718,7 +1034,7 @@ Return ONLY the commit message - no explanations, no extra text.`,
},
inputs: {
operation: { type: 'string', description: 'Operation to perform' },
credential: { type: 'string', description: 'GitLab access token' },
accessToken: { type: 'string', description: 'GitLab Personal Access Token' },
host: { type: 'string', description: 'Self-managed GitLab host (defaults to gitlab.com)' },
projectId: { type: 'string', description: 'Project ID or URL-encoded path' },
issueIid: { type: 'number', description: 'Issue internal ID' },
@@ -745,6 +1061,16 @@ Return ONLY the commit message - no explanations, no extra text.`,
mergeCommitMessage: { type: 'string', description: 'Custom merge commit message' },
perPage: { type: 'number', description: 'Results per page' },
page: { type: 'number', description: 'Page number' },
filePath: { type: 'string', description: 'Path to file in the repository' },
branch: { type: 'string', description: 'Branch name' },
content: { type: 'string', description: 'File content' },
commitMessage: { type: 'string', description: 'Commit message' },
jobId: { type: 'number', description: 'Job ID' },
path: { type: 'string', description: 'Subdirectory path for repository tree' },
recursive: { type: 'boolean', description: 'Recursively list repository tree' },
refName: { type: 'string', description: 'Branch or tag name filter' },
scope: { type: 'string', description: 'Job scope filter' },
sha: { type: 'string', description: 'Commit SHA' },
},
outputs: {
// Project outputs
@@ -761,6 +1087,21 @@ Return ONLY the commit message - no explanations, no extra text.`,
pipeline: { type: 'json', description: 'Pipeline details' },
// Note outputs
note: { type: 'json', description: 'Comment/note details' },
// Repository outputs
tree: { type: 'json', description: 'Repository tree entries' },
content: { type: 'string', description: 'File contents (decoded)' },
fileName: { type: 'string', description: 'File name' },
branches: { type: 'json', description: 'List of branches' },
commits: { type: 'json', description: 'List of commits' },
name: { type: 'string', description: 'Created branch name' },
webUrl: { type: 'string', description: 'Web URL' },
// Merge request change outputs
changes: { type: 'json', description: 'Merge request file changes/diffs' },
approvalsRequired: { type: 'number', description: 'Approvals required' },
approvalsLeft: { type: 'number', description: 'Approvals remaining' },
// Job outputs
jobs: { type: 'json', description: 'Pipeline jobs' },
log: { type: 'string', description: 'Job log output' },
// Success indicator
success: { type: 'boolean', description: 'Operation success status' },
},
+144 -8
View File
@@ -1,5 +1,5 @@
{
"updatedAt": "2026-06-18",
"updatedAt": "2026-06-25",
"integrations": [
{
"type": "onepassword",
@@ -5628,11 +5628,90 @@
{
"name": "Cancel Pipeline",
"description": "Cancel a running GitLab pipeline"
},
{
"name": "List Repository Tree",
"description": "List files and directories in a GitLab project repository"
},
{
"name": "Get File",
"description": "Get the contents of a file from a GitLab project repository"
},
{
"name": "Create File",
"description": "Create a new file in a GitLab project repository"
},
{
"name": "Update File",
"description": "Update an existing file in a GitLab project repository"
},
{
"name": "List Commits",
"description": "List commits in a GitLab project repository"
},
{
"name": "List Branches",
"description": "List branches in a GitLab project repository"
},
{
"name": "Create Branch",
"description": "Create a new branch in a GitLab project repository"
},
{
"name": "Get MR Changes",
"description": "Get the file changes (diffs) of a GitLab merge request"
},
{
"name": "Approve Merge Request",
"description": "Approve a GitLab merge request"
},
{
"name": "List Pipeline Jobs",
"description": "List jobs for a GitLab pipeline"
},
{
"name": "Get Job Log",
"description": "Get the log (trace) of a GitLab job"
},
{
"name": "Play Job",
"description": "Trigger (play) a manual GitLab job"
}
],
"operationCount": 19,
"triggers": [],
"triggerCount": 0,
"operationCount": 31,
"triggers": [
{
"id": "gitlab_push",
"name": "GitLab Push",
"description": "Trigger workflow when commits are pushed to a GitLab project"
},
{
"id": "gitlab_merge_request",
"name": "GitLab Merge Request",
"description": "Trigger workflow when a merge request is opened, updated, or merged in GitLab"
},
{
"id": "gitlab_issue",
"name": "GitLab Issue",
"description": "Trigger workflow when an issue is opened, updated, or closed in GitLab"
},
{
"id": "gitlab_pipeline",
"name": "GitLab Pipeline",
"description": "Trigger workflow when a pipeline status changes in GitLab"
},
{
"id": "gitlab_comment",
"name": "GitLab Comment",
"description": "Trigger workflow when a comment is added on a commit, merge request, or issue"
},
{
"id": "gitlab_webhook",
"name": "GitLab Event",
"description": "Trigger workflow from any GitLab webhook event"
}
],
"triggerCount": 6,
"authType": "api-key",
"category": "tools",
"integrationType": "devops",
@@ -11233,8 +11312,39 @@
}
],
"operationCount": 6,
"triggers": [],
"triggerCount": 0,
"triggers": [
{
"id": "pagerduty_incident_triggered",
"name": "PagerDuty Incident Triggered",
"description": "Trigger workflow when a new incident is triggered in PagerDuty"
},
{
"id": "pagerduty_incident_acknowledged",
"name": "PagerDuty Incident Acknowledged",
"description": "Trigger workflow when an incident is acknowledged in PagerDuty"
},
{
"id": "pagerduty_incident_resolved",
"name": "PagerDuty Incident Resolved",
"description": "Trigger workflow when an incident is resolved in PagerDuty"
},
{
"id": "pagerduty_incident_escalated",
"name": "PagerDuty Incident Escalated",
"description": "Trigger workflow when an incident is escalated in PagerDuty"
},
{
"id": "pagerduty_incident_reassigned",
"name": "PagerDuty Incident Reassigned",
"description": "Trigger workflow when an incident is reassigned in PagerDuty"
},
{
"id": "pagerduty_webhook",
"name": "PagerDuty Incident Event",
"description": "Trigger workflow from any PagerDuty incident event"
}
],
"triggerCount": 6,
"authType": "api-key",
"category": "tools",
"integrationType": "observability",
@@ -18289,8 +18399,34 @@
}
],
"operationCount": 26,
"triggers": [],
"triggerCount": 0,
"triggers": [
{
"id": "zendesk_ticket_created",
"name": "Zendesk Ticket Created",
"description": "Trigger workflow when a new ticket is created in Zendesk"
},
{
"id": "zendesk_ticket_status_changed",
"name": "Zendesk Ticket Status Changed",
"description": "Trigger workflow when a ticket status changes in Zendesk"
},
{
"id": "zendesk_ticket_comment_added",
"name": "Zendesk Ticket Comment Added",
"description": "Trigger workflow when a comment is added to a Zendesk ticket"
},
{
"id": "zendesk_ticket_priority_changed",
"name": "Zendesk Ticket Priority Changed",
"description": "Trigger workflow when a ticket priority changes in Zendesk"
},
{
"id": "zendesk_webhook",
"name": "Zendesk Ticket Event",
"description": "Trigger workflow from any Zendesk ticket event"
}
],
"triggerCount": 5,
"authType": "api-key",
"category": "tools",
"integrationType": "support",
@@ -0,0 +1,105 @@
import type {
GitLabApproveMergeRequestParams,
GitLabApproveMergeRequestResponse,
} from '@/tools/gitlab/types'
import { getGitLabApiBase } from '@/tools/gitlab/utils'
import type { ToolConfig } from '@/tools/types'
export const gitlabApproveMergeRequestTool: ToolConfig<
GitLabApproveMergeRequestParams,
GitLabApproveMergeRequestResponse
> = {
id: 'gitlab_approve_merge_request',
name: 'GitLab Approve Merge Request',
description: 'Approve a GitLab merge request',
version: '1.0.0',
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitLab Personal Access Token',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Self-managed GitLab host (e.g. gitlab.example.com). Defaults to gitlab.com.',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or URL-encoded path',
},
mergeRequestIid: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Merge request internal ID (IID)',
},
sha: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'HEAD SHA of the merge request to approve',
},
},
request: {
url: (params) => {
const encodedId = encodeURIComponent(String(params.projectId).trim())
return `${getGitLabApiBase(params.host)}/projects/${encodedId}/merge_requests/${params.mergeRequestIid}/approve`
},
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/json',
'PRIVATE-TOKEN': params.accessToken,
}),
body: (params) => {
const body: Record<string, unknown> = {}
if (params.sha) body.sha = params.sha
return body
},
},
transformResponse: async (response) => {
if (!response.ok) {
const errorText = await response.text()
return {
success: false,
error: `GitLab API error: ${response.status} ${errorText}`,
output: {},
}
}
const data = await response.json()
return {
success: true,
output: {
approvalsRequired: data.approvals_required ?? null,
approvalsLeft: data.approvals_left ?? null,
approvedBy: data.approved_by ?? [],
},
}
},
outputs: {
approvalsRequired: {
type: 'number',
description: 'Number of approvals required',
},
approvalsLeft: {
type: 'number',
description: 'Number of approvals still needed',
},
approvedBy: {
type: 'array',
description: 'List of approvers',
},
},
}
+1 -1
View File
@@ -40,7 +40,7 @@ export const gitlabCancelPipelineTool: ToolConfig<
request: {
url: (params) => {
const encodedId = encodeURIComponent(String(params.projectId))
const encodedId = encodeURIComponent(String(params.projectId).trim())
return `${getGitLabApiBase(params.host)}/projects/${encodedId}/pipelines/${params.pipelineId}/cancel`
},
method: 'POST',
+102
View File
@@ -0,0 +1,102 @@
import type { GitLabCreateBranchParams, GitLabCreateBranchResponse } from '@/tools/gitlab/types'
import { getGitLabApiBase } from '@/tools/gitlab/utils'
import type { ToolConfig } from '@/tools/types'
export const gitlabCreateBranchTool: ToolConfig<
GitLabCreateBranchParams,
GitLabCreateBranchResponse
> = {
id: 'gitlab_create_branch',
name: 'GitLab Create Branch',
description: 'Create a new branch in a GitLab project repository',
version: '1.0.0',
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitLab Personal Access Token',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Self-managed GitLab host (e.g. gitlab.example.com). Defaults to gitlab.com.',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or URL-encoded path',
},
branch: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Name of the new branch',
},
ref: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Source branch/tag/SHA',
},
},
request: {
url: (params) => {
const encodedId = encodeURIComponent(String(params.projectId).trim())
const queryParams = new URLSearchParams()
queryParams.append('branch', String(params.branch))
queryParams.append('ref', String(params.ref))
return `${getGitLabApiBase(params.host)}/projects/${encodedId}/repository/branches?${queryParams.toString()}`
},
method: 'POST',
headers: (params) => ({
'PRIVATE-TOKEN': params.accessToken,
}),
},
transformResponse: async (response) => {
if (!response.ok) {
const errorText = await response.text()
return {
success: false,
error: `GitLab API error: ${response.status} ${errorText}`,
output: {},
}
}
const data = await response.json()
return {
success: true,
output: {
name: data.name ?? null,
webUrl: data.web_url ?? null,
protected: data.protected ?? null,
commit: data.commit ?? null,
},
}
},
outputs: {
name: {
type: 'string',
description: 'The created branch name',
},
webUrl: {
type: 'string',
description: 'The web URL of the branch',
},
protected: {
type: 'boolean',
description: 'Whether the branch is protected',
},
commit: {
type: 'object',
description: 'The commit the branch points to',
},
},
}
+106
View File
@@ -0,0 +1,106 @@
import type { GitLabCreateFileParams, GitLabCreateFileResponse } from '@/tools/gitlab/types'
import { getGitLabApiBase } from '@/tools/gitlab/utils'
import type { ToolConfig } from '@/tools/types'
export const gitlabCreateFileTool: ToolConfig<GitLabCreateFileParams, GitLabCreateFileResponse> = {
id: 'gitlab_create_file',
name: 'GitLab Create File',
description: 'Create a new file in a GitLab project repository',
version: '1.0.0',
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitLab Personal Access Token',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Self-managed GitLab host (e.g. gitlab.example.com). Defaults to gitlab.com.',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or URL-encoded path',
},
filePath: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Path to the file in the repository',
},
branch: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Branch to commit the new file to',
},
content: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'File content',
},
commitMessage: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Commit message',
},
},
request: {
url: (params) => {
const encodedId = encodeURIComponent(String(params.projectId).trim())
const encodedPath = encodeURIComponent(String(params.filePath))
return `${getGitLabApiBase(params.host)}/projects/${encodedId}/repository/files/${encodedPath}`
},
method: 'POST',
headers: (params) => ({
'Content-Type': 'application/json',
'PRIVATE-TOKEN': params.accessToken,
}),
body: (params) => ({
branch: params.branch,
content: params.content,
commit_message: params.commitMessage,
encoding: 'text',
}),
},
transformResponse: async (response) => {
if (!response.ok) {
const errorText = await response.text()
return {
success: false,
error: `GitLab API error: ${response.status} ${errorText}`,
output: {},
}
}
const data = await response.json()
return {
success: true,
output: {
filePath: data.file_path ?? null,
branch: data.branch ?? null,
},
}
},
outputs: {
filePath: {
type: 'string',
description: 'The created file path',
},
branch: {
type: 'string',
description: 'The branch the file was committed to',
},
},
}
+1 -1
View File
@@ -74,7 +74,7 @@ export const gitlabCreateIssueTool: ToolConfig<GitLabCreateIssueParams, GitLabCr
request: {
url: (params) => {
const encodedId = encodeURIComponent(String(params.projectId))
const encodedId = encodeURIComponent(String(params.projectId).trim())
return `${getGitLabApiBase(params.host)}/projects/${encodedId}/issues`
},
method: 'POST',
+1 -1
View File
@@ -46,7 +46,7 @@ export const gitlabCreateIssueNoteTool: ToolConfig<
request: {
url: (params) => {
const encodedId = encodeURIComponent(String(params.projectId))
const encodedId = encodeURIComponent(String(params.projectId).trim())
return `${getGitLabApiBase(params.host)}/projects/${encodedId}/issues/${params.issueIid}/notes`
},
method: 'POST',
@@ -97,7 +97,7 @@ export const gitlabCreateMergeRequestTool: ToolConfig<
request: {
url: (params) => {
const encodedId = encodeURIComponent(String(params.projectId))
const encodedId = encodeURIComponent(String(params.projectId).trim())
return `${getGitLabApiBase(params.host)}/projects/${encodedId}/merge_requests`
},
method: 'POST',
@@ -49,7 +49,7 @@ export const gitlabCreateMergeRequestNoteTool: ToolConfig<
request: {
url: (params) => {
const encodedId = encodeURIComponent(String(params.projectId))
const encodedId = encodeURIComponent(String(params.projectId).trim())
return `${getGitLabApiBase(params.host)}/projects/${encodedId}/merge_requests/${params.mergeRequestIid}/notes`
},
method: 'POST',
+1 -1
View File
@@ -47,7 +47,7 @@ export const gitlabCreatePipelineTool: ToolConfig<
request: {
url: (params) => {
const encodedId = encodeURIComponent(String(params.projectId))
const encodedId = encodeURIComponent(String(params.projectId).trim())
return `${getGitLabApiBase(params.host)}/projects/${encodedId}/pipeline`
},
method: 'POST',
+1 -1
View File
@@ -38,7 +38,7 @@ export const gitlabDeleteIssueTool: ToolConfig<GitLabDeleteIssueParams, GitLabDe
request: {
url: (params) => {
const encodedId = encodeURIComponent(String(params.projectId))
const encodedId = encodeURIComponent(String(params.projectId).trim())
return `${getGitLabApiBase(params.host)}/projects/${encodedId}/issues/${params.issueIid}`
},
method: 'DELETE',
+114
View File
@@ -0,0 +1,114 @@
import type { GitLabGetFileParams, GitLabGetFileResponse } from '@/tools/gitlab/types'
import { getGitLabApiBase } from '@/tools/gitlab/utils'
import type { ToolConfig } from '@/tools/types'
export const gitlabGetFileTool: ToolConfig<GitLabGetFileParams, GitLabGetFileResponse> = {
id: 'gitlab_get_file',
name: 'GitLab Get File',
description: 'Get the contents of a file from a GitLab project repository',
version: '1.0.0',
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitLab Personal Access Token',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Self-managed GitLab host (e.g. gitlab.example.com). Defaults to gitlab.com.',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or URL-encoded path',
},
filePath: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Path to the file in the repository',
},
ref: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Branch, tag, or commit SHA',
},
},
request: {
url: (params) => {
const encodedId = encodeURIComponent(String(params.projectId).trim())
const encodedPath = encodeURIComponent(String(params.filePath))
const ref = encodeURIComponent(String(params.ref))
return `${getGitLabApiBase(params.host)}/projects/${encodedId}/repository/files/${encodedPath}?ref=${ref}`
},
method: 'GET',
headers: (params) => ({
'PRIVATE-TOKEN': params.accessToken,
}),
},
transformResponse: async (response) => {
if (!response.ok) {
const errorText = await response.text()
return {
success: false,
error: `GitLab API error: ${response.status} ${errorText}`,
output: {},
}
}
const data = await response.json()
const decoded = Buffer.from(data.content ?? '', 'base64').toString('utf-8')
return {
success: true,
output: {
filePath: data.file_path ?? null,
fileName: data.file_name ?? null,
size: data.size ?? null,
ref: data.ref ?? null,
blobId: data.blob_id ?? null,
lastCommitId: data.last_commit_id ?? null,
content: decoded,
},
}
},
outputs: {
filePath: {
type: 'string',
description: 'The file path',
},
fileName: {
type: 'string',
description: 'The file name',
},
size: {
type: 'number',
description: 'The file size in bytes',
},
ref: {
type: 'string',
description: 'The branch, tag, or commit SHA',
},
blobId: {
type: 'string',
description: 'The blob ID',
},
lastCommitId: {
type: 'string',
description: 'The last commit ID that modified the file',
},
content: {
type: 'string',
description: 'The decoded file content',
},
},
}
+1 -1
View File
@@ -37,7 +37,7 @@ export const gitlabGetIssueTool: ToolConfig<GitLabGetIssueParams, GitLabGetIssue
request: {
url: (params) => {
const encodedId = encodeURIComponent(String(params.projectId))
const encodedId = encodeURIComponent(String(params.projectId).trim())
return `${getGitLabApiBase(params.host)}/projects/${encodedId}/issues/${params.issueIid}`
},
method: 'GET',
+75
View File
@@ -0,0 +1,75 @@
import type { GitLabGetJobLogParams, GitLabGetJobLogResponse } from '@/tools/gitlab/types'
import { getGitLabApiBase } from '@/tools/gitlab/utils'
import type { ToolConfig } from '@/tools/types'
export const gitlabGetJobLogTool: ToolConfig<GitLabGetJobLogParams, GitLabGetJobLogResponse> = {
id: 'gitlab_get_job_log',
name: 'GitLab Get Job Log',
description: 'Get the log (trace) of a GitLab job',
version: '1.0.0',
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitLab Personal Access Token',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Self-managed GitLab host (e.g. gitlab.example.com). Defaults to gitlab.com.',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or URL-encoded path',
},
jobId: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Job ID',
},
},
request: {
url: (params) => {
const encodedId = encodeURIComponent(String(params.projectId).trim())
return `${getGitLabApiBase(params.host)}/projects/${encodedId}/jobs/${params.jobId}/trace`
},
method: 'GET',
headers: (params) => ({
'PRIVATE-TOKEN': params.accessToken,
}),
},
transformResponse: async (response) => {
if (!response.ok) {
const errorText = await response.text()
return {
success: false,
error: `GitLab API error: ${response.status} ${errorText}`,
output: {},
}
}
const log = await response.text()
return {
success: true,
output: {
log,
},
}
},
outputs: {
log: {
type: 'string',
description: 'The job log (trace) output',
},
},
}
+1 -1
View File
@@ -43,7 +43,7 @@ export const gitlabGetMergeRequestTool: ToolConfig<
request: {
url: (params) => {
const encodedId = encodeURIComponent(String(params.projectId))
const encodedId = encodeURIComponent(String(params.projectId).trim())
return `${getGitLabApiBase(params.host)}/projects/${encodedId}/merge_requests/${params.mergeRequestIid}`
},
method: 'GET',
@@ -0,0 +1,98 @@
import type {
GitLabGetMergeRequestChangesParams,
GitLabGetMergeRequestChangesResponse,
} from '@/tools/gitlab/types'
import { getGitLabApiBase } from '@/tools/gitlab/utils'
import type { ToolConfig } from '@/tools/types'
export const gitlabGetMergeRequestChangesTool: ToolConfig<
GitLabGetMergeRequestChangesParams,
GitLabGetMergeRequestChangesResponse
> = {
id: 'gitlab_get_merge_request_changes',
name: 'GitLab Get Merge Request Changes',
description: 'Get the file changes (diffs) of a GitLab merge request',
version: '1.0.0',
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitLab Personal Access Token',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Self-managed GitLab host (e.g. gitlab.example.com). Defaults to gitlab.com.',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or URL-encoded path',
},
mergeRequestIid: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Merge request internal ID (IID)',
},
},
request: {
/**
* Uses the `/diffs` endpoint (the `/changes` endpoint was deprecated in
* GitLab 15.7 and removed in 18.0). `/diffs` returns the diff array directly
* and is paginated; we request the max page size (100) to return the changes
* in a single call, which covers the vast majority of merge requests.
*/
url: (params) => {
const encodedId = encodeURIComponent(String(params.projectId).trim())
return `${getGitLabApiBase(params.host)}/projects/${encodedId}/merge_requests/${params.mergeRequestIid}/diffs?per_page=100`
},
method: 'GET',
headers: (params) => ({
'PRIVATE-TOKEN': params.accessToken,
}),
},
transformResponse: async (response, params) => {
if (!response.ok) {
const errorText = await response.text()
return {
success: false,
error: `GitLab API error: ${response.status} ${errorText}`,
output: {},
}
}
const data = await response.json()
const changes = Array.isArray(data) ? data : []
return {
success: true,
output: {
mergeRequestIid: params?.mergeRequestIid ?? null,
changes,
changesCount: changes.length,
},
}
},
outputs: {
mergeRequestIid: {
type: 'number',
description: 'The merge request internal ID (IID)',
},
changes: {
type: 'array',
description: 'List of file changes (diffs)',
},
changesCount: {
type: 'number',
description: 'Number of changed files returned',
},
},
}
+1 -1
View File
@@ -38,7 +38,7 @@ export const gitlabGetPipelineTool: ToolConfig<GitLabGetPipelineParams, GitLabGe
request: {
url: (params) => {
const encodedId = encodeURIComponent(String(params.projectId))
const encodedId = encodeURIComponent(String(params.projectId).trim())
return `${getGitLabApiBase(params.host)}/projects/${encodedId}/pipelines/${params.pipelineId}`
},
method: 'GET',
+1 -1
View File
@@ -31,7 +31,7 @@ export const gitlabGetProjectTool: ToolConfig<GitLabGetProjectParams, GitLabGetP
request: {
url: (params) => {
const encodedId = encodeURIComponent(String(params.projectId))
const encodedId = encodeURIComponent(String(params.projectId).trim())
return `${getGitLabApiBase(params.host)}/projects/${encodedId}`
},
method: 'GET',
+28
View File
@@ -1,20 +1,32 @@
import { gitlabApproveMergeRequestTool } from '@/tools/gitlab/approve_merge_request'
import { gitlabCancelPipelineTool } from '@/tools/gitlab/cancel_pipeline'
import { gitlabCreateBranchTool } from '@/tools/gitlab/create_branch'
import { gitlabCreateFileTool } from '@/tools/gitlab/create_file'
import { gitlabCreateIssueTool } from '@/tools/gitlab/create_issue'
import { gitlabCreateIssueNoteTool } from '@/tools/gitlab/create_issue_note'
import { gitlabCreateMergeRequestTool } from '@/tools/gitlab/create_merge_request'
import { gitlabCreateMergeRequestNoteTool } from '@/tools/gitlab/create_merge_request_note'
import { gitlabCreatePipelineTool } from '@/tools/gitlab/create_pipeline'
import { gitlabDeleteIssueTool } from '@/tools/gitlab/delete_issue'
import { gitlabGetFileTool } from '@/tools/gitlab/get_file'
import { gitlabGetIssueTool } from '@/tools/gitlab/get_issue'
import { gitlabGetJobLogTool } from '@/tools/gitlab/get_job_log'
import { gitlabGetMergeRequestTool } from '@/tools/gitlab/get_merge_request'
import { gitlabGetMergeRequestChangesTool } from '@/tools/gitlab/get_merge_request_changes'
import { gitlabGetPipelineTool } from '@/tools/gitlab/get_pipeline'
import { gitlabGetProjectTool } from '@/tools/gitlab/get_project'
import { gitlabListBranchesTool } from '@/tools/gitlab/list_branches'
import { gitlabListCommitsTool } from '@/tools/gitlab/list_commits'
import { gitlabListIssuesTool } from '@/tools/gitlab/list_issues'
import { gitlabListMergeRequestsTool } from '@/tools/gitlab/list_merge_requests'
import { gitlabListPipelineJobsTool } from '@/tools/gitlab/list_pipeline_jobs'
import { gitlabListPipelinesTool } from '@/tools/gitlab/list_pipelines'
import { gitlabListProjectsTool } from '@/tools/gitlab/list_projects'
import { gitlabListRepositoryTreeTool } from '@/tools/gitlab/list_repository_tree'
import { gitlabMergeMergeRequestTool } from '@/tools/gitlab/merge_merge_request'
import { gitlabPlayJobTool } from '@/tools/gitlab/play_job'
import { gitlabRetryPipelineTool } from '@/tools/gitlab/retry_pipeline'
import { gitlabUpdateFileTool } from '@/tools/gitlab/update_file'
import { gitlabUpdateIssueTool } from '@/tools/gitlab/update_issue'
import { gitlabUpdateMergeRequestTool } from '@/tools/gitlab/update_merge_request'
@@ -36,10 +48,26 @@ export {
gitlabUpdateMergeRequestTool,
gitlabMergeMergeRequestTool,
gitlabCreateMergeRequestNoteTool,
gitlabGetMergeRequestChangesTool,
gitlabApproveMergeRequestTool,
// Pipelines
gitlabListPipelinesTool,
gitlabGetPipelineTool,
gitlabCreatePipelineTool,
gitlabRetryPipelineTool,
gitlabCancelPipelineTool,
// Jobs
gitlabListPipelineJobsTool,
gitlabGetJobLogTool,
gitlabPlayJobTool,
// Repository Files & Tree
gitlabListRepositoryTreeTool,
gitlabGetFileTool,
gitlabCreateFileTool,
gitlabUpdateFileTool,
// Branches
gitlabListBranchesTool,
gitlabCreateBranchTool,
// Commits
gitlabListCommitsTool,
}
+103
View File
@@ -0,0 +1,103 @@
import type { GitLabListBranchesParams, GitLabListBranchesResponse } from '@/tools/gitlab/types'
import { getGitLabApiBase } from '@/tools/gitlab/utils'
import type { ToolConfig } from '@/tools/types'
export const gitlabListBranchesTool: ToolConfig<
GitLabListBranchesParams,
GitLabListBranchesResponse
> = {
id: 'gitlab_list_branches',
name: 'GitLab List Branches',
description: 'List branches in a GitLab project repository',
version: '1.0.0',
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitLab Personal Access Token',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Self-managed GitLab host (e.g. gitlab.example.com). Defaults to gitlab.com.',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or URL-encoded path',
},
search: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter branches by name',
},
perPage: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of results per page (default 20, max 100)',
},
page: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number for pagination',
},
},
request: {
url: (params) => {
const encodedId = encodeURIComponent(String(params.projectId).trim())
const queryParams = new URLSearchParams()
if (params.search) queryParams.append('search', params.search)
if (params.perPage) queryParams.append('per_page', String(params.perPage))
if (params.page) queryParams.append('page', String(params.page))
const query = queryParams.toString()
return `${getGitLabApiBase(params.host)}/projects/${encodedId}/repository/branches${query ? `?${query}` : ''}`
},
method: 'GET',
headers: (params) => ({
'PRIVATE-TOKEN': params.accessToken,
}),
},
transformResponse: async (response) => {
if (!response.ok) {
const errorText = await response.text()
return {
success: false,
error: `GitLab API error: ${response.status} ${errorText}`,
output: {},
}
}
const branches = await response.json()
const total = response.headers.get('x-total')
return {
success: true,
output: {
branches: branches ?? [],
total: total ? Number.parseInt(total, 10) : (branches?.length ?? 0),
},
}
},
outputs: {
branches: {
type: 'array',
description: 'List of branches',
},
total: {
type: 'number',
description: 'Total number of branches',
},
},
}
+129
View File
@@ -0,0 +1,129 @@
import type { GitLabListCommitsParams, GitLabListCommitsResponse } from '@/tools/gitlab/types'
import { getGitLabApiBase } from '@/tools/gitlab/utils'
import type { ToolConfig } from '@/tools/types'
export const gitlabListCommitsTool: ToolConfig<GitLabListCommitsParams, GitLabListCommitsResponse> =
{
id: 'gitlab_list_commits',
name: 'GitLab List Commits',
description: 'List commits in a GitLab project repository',
version: '1.0.0',
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitLab Personal Access Token',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Self-managed GitLab host (e.g. gitlab.example.com). Defaults to gitlab.com.',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or URL-encoded path',
},
refName: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Branch, tag, or revision range to list commits from',
},
since: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Only commits after this ISO 8601 date',
},
until: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Only commits before this ISO 8601 date',
},
path: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Only commits affecting this file path',
},
author: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter commits by author',
},
perPage: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of results per page (default 20, max 100)',
},
page: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number for pagination',
},
},
request: {
url: (params) => {
const encodedId = encodeURIComponent(String(params.projectId).trim())
const queryParams = new URLSearchParams()
if (params.refName) queryParams.append('ref_name', params.refName)
if (params.since) queryParams.append('since', params.since)
if (params.until) queryParams.append('until', params.until)
if (params.path) queryParams.append('path', params.path)
if (params.author) queryParams.append('author', params.author)
if (params.perPage) queryParams.append('per_page', String(params.perPage))
if (params.page) queryParams.append('page', String(params.page))
const query = queryParams.toString()
return `${getGitLabApiBase(params.host)}/projects/${encodedId}/repository/commits${query ? `?${query}` : ''}`
},
method: 'GET',
headers: (params) => ({
'PRIVATE-TOKEN': params.accessToken,
}),
},
transformResponse: async (response) => {
if (!response.ok) {
const errorText = await response.text()
return {
success: false,
error: `GitLab API error: ${response.status} ${errorText}`,
output: {},
}
}
const commits = await response.json()
const total = response.headers.get('x-total')
return {
success: true,
output: {
commits: commits ?? [],
total: total ? Number.parseInt(total, 10) : (commits?.length ?? 0),
},
}
},
outputs: {
commits: {
type: 'array',
description: 'List of commits',
},
total: {
type: 'number',
description: 'Total number of commits',
},
},
}
+1 -1
View File
@@ -85,7 +85,7 @@ export const gitlabListIssuesTool: ToolConfig<GitLabListIssuesParams, GitLabList
request: {
url: (params) => {
const encodedId = encodeURIComponent(String(params.projectId))
const encodedId = encodeURIComponent(String(params.projectId).trim())
const queryParams = new URLSearchParams()
if (params.state) queryParams.append('state', params.state)
+1 -1
View File
@@ -85,7 +85,7 @@ export const gitlabListMergeRequestsTool: ToolConfig<
request: {
url: (params) => {
const encodedId = encodeURIComponent(String(params.projectId))
const encodedId = encodeURIComponent(String(params.projectId).trim())
const queryParams = new URLSearchParams()
if (params.state) queryParams.append('state', params.state)
+119
View File
@@ -0,0 +1,119 @@
import type {
GitLabListPipelineJobsParams,
GitLabListPipelineJobsResponse,
} from '@/tools/gitlab/types'
import { getGitLabApiBase } from '@/tools/gitlab/utils'
import type { ToolConfig } from '@/tools/types'
export const gitlabListPipelineJobsTool: ToolConfig<
GitLabListPipelineJobsParams,
GitLabListPipelineJobsResponse
> = {
id: 'gitlab_list_pipeline_jobs',
name: 'GitLab List Pipeline Jobs',
description: 'List jobs for a GitLab pipeline',
version: '1.0.0',
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitLab Personal Access Token',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Self-managed GitLab host (e.g. gitlab.example.com). Defaults to gitlab.com.',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or URL-encoded path',
},
pipelineId: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Pipeline ID',
},
scope: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter jobs by scope (e.g. created, running, success, failed)',
},
includeRetried: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether to include retried jobs',
},
perPage: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of results per page (default 20, max 100)',
},
page: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number for pagination',
},
},
request: {
url: (params) => {
const encodedId = encodeURIComponent(String(params.projectId).trim())
const queryParams = new URLSearchParams()
if (params.scope) queryParams.append('scope', params.scope)
if (params.includeRetried) queryParams.append('include_retried', 'true')
if (params.perPage) queryParams.append('per_page', String(params.perPage))
if (params.page) queryParams.append('page', String(params.page))
const query = queryParams.toString()
return `${getGitLabApiBase(params.host)}/projects/${encodedId}/pipelines/${params.pipelineId}/jobs${query ? `?${query}` : ''}`
},
method: 'GET',
headers: (params) => ({
'PRIVATE-TOKEN': params.accessToken,
}),
},
transformResponse: async (response) => {
if (!response.ok) {
const errorText = await response.text()
return {
success: false,
error: `GitLab API error: ${response.status} ${errorText}`,
output: {},
}
}
const jobs = await response.json()
const total = response.headers.get('x-total')
return {
success: true,
output: {
jobs: jobs ?? [],
total: total ? Number.parseInt(total, 10) : (jobs?.length ?? 0),
},
}
},
outputs: {
jobs: {
type: 'array',
description: 'List of pipeline jobs',
},
total: {
type: 'number',
description: 'Total number of jobs',
},
},
}
+1 -1
View File
@@ -71,7 +71,7 @@ export const gitlabListPipelinesTool: ToolConfig<
request: {
url: (params) => {
const encodedId = encodeURIComponent(String(params.projectId))
const encodedId = encodeURIComponent(String(params.projectId).trim())
const queryParams = new URLSearchParams()
if (params.ref) queryParams.append('ref', params.ref)
@@ -0,0 +1,120 @@
import type {
GitLabListRepositoryTreeParams,
GitLabListRepositoryTreeResponse,
} from '@/tools/gitlab/types'
import { getGitLabApiBase } from '@/tools/gitlab/utils'
import type { ToolConfig } from '@/tools/types'
export const gitlabListRepositoryTreeTool: ToolConfig<
GitLabListRepositoryTreeParams,
GitLabListRepositoryTreeResponse
> = {
id: 'gitlab_list_repository_tree',
name: 'GitLab List Repository Tree',
description: 'List files and directories in a GitLab project repository',
version: '1.0.0',
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitLab Personal Access Token',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Self-managed GitLab host (e.g. gitlab.example.com). Defaults to gitlab.com.',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or URL-encoded path',
},
path: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Path inside the repository to list',
},
ref: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Branch, tag, or commit SHA to list from',
},
recursive: {
type: 'boolean',
required: false,
visibility: 'user-or-llm',
description: 'Whether to list files recursively',
},
perPage: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of results per page (default 20, max 100)',
},
page: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Page number for pagination',
},
},
request: {
url: (params) => {
const encodedId = encodeURIComponent(String(params.projectId).trim())
const queryParams = new URLSearchParams()
if (params.path) queryParams.append('path', params.path)
if (params.ref) queryParams.append('ref', params.ref)
if (params.recursive) queryParams.append('recursive', 'true')
if (params.perPage) queryParams.append('per_page', String(params.perPage))
if (params.page) queryParams.append('page', String(params.page))
const query = queryParams.toString()
return `${getGitLabApiBase(params.host)}/projects/${encodedId}/repository/tree${query ? `?${query}` : ''}`
},
method: 'GET',
headers: (params) => ({
'PRIVATE-TOKEN': params.accessToken,
}),
},
transformResponse: async (response) => {
if (!response.ok) {
const errorText = await response.text()
return {
success: false,
error: `GitLab API error: ${response.status} ${errorText}`,
output: {},
}
}
const tree = await response.json()
const total = response.headers.get('x-total')
return {
success: true,
output: {
tree: tree ?? [],
total: total ? Number.parseInt(total, 10) : (tree?.length ?? 0),
},
}
},
outputs: {
tree: {
type: 'array',
description: 'List of repository tree entries',
},
total: {
type: 'number',
description: 'Total number of tree entries',
},
},
}
+1 -1
View File
@@ -73,7 +73,7 @@ export const gitlabMergeMergeRequestTool: ToolConfig<
request: {
url: (params) => {
const encodedId = encodeURIComponent(String(params.projectId))
const encodedId = encodeURIComponent(String(params.projectId).trim())
return `${getGitLabApiBase(params.host)}/projects/${encodedId}/merge_requests/${params.mergeRequestIid}/merge`
},
method: 'PUT',
+90
View File
@@ -0,0 +1,90 @@
import type { GitLabPlayJobParams, GitLabPlayJobResponse } from '@/tools/gitlab/types'
import { getGitLabApiBase } from '@/tools/gitlab/utils'
import type { ToolConfig } from '@/tools/types'
export const gitlabPlayJobTool: ToolConfig<GitLabPlayJobParams, GitLabPlayJobResponse> = {
id: 'gitlab_play_job',
name: 'GitLab Play Job',
description: 'Trigger (play) a manual GitLab job',
version: '1.0.0',
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitLab Personal Access Token',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Self-managed GitLab host (e.g. gitlab.example.com). Defaults to gitlab.com.',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or URL-encoded path',
},
jobId: {
type: 'number',
required: true,
visibility: 'user-or-llm',
description: 'Job ID',
},
},
request: {
url: (params) => {
const encodedId = encodeURIComponent(String(params.projectId).trim())
return `${getGitLabApiBase(params.host)}/projects/${encodedId}/jobs/${params.jobId}/play`
},
method: 'POST',
headers: (params) => ({
'PRIVATE-TOKEN': params.accessToken,
}),
},
transformResponse: async (response) => {
if (!response.ok) {
const errorText = await response.text()
return {
success: false,
error: `GitLab API error: ${response.status} ${errorText}`,
output: {},
}
}
const data = await response.json()
return {
success: true,
output: {
id: data.id ?? null,
name: data.name ?? null,
status: data.status ?? null,
webUrl: data.web_url ?? null,
},
}
},
outputs: {
id: {
type: 'number',
description: 'The job ID',
},
name: {
type: 'string',
description: 'The job name',
},
status: {
type: 'string',
description: 'The job status',
},
webUrl: {
type: 'string',
description: 'The web URL of the job',
},
},
}
+1 -1
View File
@@ -40,7 +40,7 @@ export const gitlabRetryPipelineTool: ToolConfig<
request: {
url: (params) => {
const encodedId = encodeURIComponent(String(params.projectId))
const encodedId = encodeURIComponent(String(params.projectId).trim())
return `${getGitLabApiBase(params.host)}/projects/${encodedId}/pipelines/${params.pipelineId}/retry`
},
method: 'POST',
+207 -27
View File
@@ -1,7 +1,5 @@
import type { ToolResponse } from '@/tools/types'
// ===== Core Types =====
interface GitLabProject {
id: number
name: string
@@ -124,6 +122,46 @@ interface GitLabPipeline {
}
}
interface GitLabTreeEntry {
id: string
name: string
type: string
path: string
mode: string
}
interface GitLabCommit {
id: string
short_id: string
title: string
message: string
author_name: string
authored_date: string
created_at: string
web_url: string
}
interface GitLabJob {
id: number
name: string
stage: string
status: string
started_at?: string | null
finished_at?: string | null
duration?: number | null
web_url: string
ref?: string
}
interface GitLabMergeRequestChange {
old_path: string
new_path: string
diff: string
new_file: boolean
deleted_file: boolean
renamed_file: boolean
}
interface GitLabBranch {
name: string
merged: boolean
@@ -190,8 +228,6 @@ interface GitLabMilestone {
web_url: string
}
// ===== Common Parameters =====
interface GitLabBaseParams {
accessToken: string
/**
@@ -201,8 +237,6 @@ interface GitLabBaseParams {
host?: string
}
// ===== Project Parameters =====
export interface GitLabListProjectsParams extends GitLabBaseParams {
owned?: boolean
membership?: boolean
@@ -218,8 +252,6 @@ export interface GitLabGetProjectParams extends GitLabBaseParams {
projectId: string | number
}
// ===== Issue Parameters =====
export interface GitLabListIssuesParams extends GitLabBaseParams {
projectId: string | number
state?: 'opened' | 'closed' | 'all'
@@ -267,8 +299,6 @@ export interface GitLabDeleteIssueParams extends GitLabBaseParams {
issueIid: number
}
// ===== Merge Request Parameters =====
export interface GitLabListMergeRequestsParams extends GitLabBaseParams {
projectId: string | number
state?: 'opened' | 'closed' | 'merged' | 'all'
@@ -325,8 +355,6 @@ export interface GitLabMergeMergeRequestParams extends GitLabBaseParams {
mergeWhenPipelineSucceeds?: boolean
}
// ===== Pipeline Parameters =====
export interface GitLabListPipelinesParams extends GitLabBaseParams {
projectId: string | number
ref?: string
@@ -369,9 +397,7 @@ export interface GitLabCancelPipelineParams extends GitLabBaseParams {
pipelineId: number
}
// ===== Branch Parameters =====
interface GitLabListBranchesParams extends GitLabBaseParams {
export interface GitLabListBranchesParams extends GitLabBaseParams {
projectId: string | number
search?: string
perPage?: number
@@ -383,7 +409,7 @@ interface GitLabGetBranchParams extends GitLabBaseParams {
branch: string
}
interface GitLabCreateBranchParams extends GitLabBaseParams {
export interface GitLabCreateBranchParams extends GitLabBaseParams {
projectId: string | number
branch: string
ref: string
@@ -394,7 +420,78 @@ interface GitLabDeleteBranchParams extends GitLabBaseParams {
branch: string
}
// ===== Note/Comment Parameters =====
export interface GitLabListRepositoryTreeParams extends GitLabBaseParams {
projectId: string | number
path?: string
ref?: string
recursive?: boolean
perPage?: number
page?: number
}
export interface GitLabGetFileParams extends GitLabBaseParams {
projectId: string | number
filePath: string
ref: string
}
export interface GitLabCreateFileParams extends GitLabBaseParams {
projectId: string | number
filePath: string
branch: string
content: string
commitMessage: string
}
export interface GitLabUpdateFileParams extends GitLabBaseParams {
projectId: string | number
filePath: string
branch: string
content: string
commitMessage: string
lastCommitId?: string
}
export interface GitLabListCommitsParams extends GitLabBaseParams {
projectId: string | number
refName?: string
since?: string
until?: string
path?: string
author?: string
perPage?: number
page?: number
}
export interface GitLabGetMergeRequestChangesParams extends GitLabBaseParams {
projectId: string | number
mergeRequestIid: number
}
export interface GitLabApproveMergeRequestParams extends GitLabBaseParams {
projectId: string | number
mergeRequestIid: number
sha?: string
}
export interface GitLabListPipelineJobsParams extends GitLabBaseParams {
projectId: string | number
pipelineId: number
scope?: string
includeRetried?: boolean
perPage?: number
page?: number
}
export interface GitLabGetJobLogParams extends GitLabBaseParams {
projectId: string | number
jobId: number
}
export interface GitLabPlayJobParams extends GitLabBaseParams {
projectId: string | number
jobId: number
}
interface GitLabListIssueNotesParams extends GitLabBaseParams {
projectId: string | number
@@ -426,8 +523,6 @@ export interface GitLabCreateMergeRequestNoteParams extends GitLabBaseParams {
body: string
}
// ===== Label Parameters =====
interface GitLabListLabelsParams extends GitLabBaseParams {
projectId: string | number
search?: string
@@ -442,8 +537,6 @@ interface GitLabCreateLabelParams extends GitLabBaseParams {
description?: string
}
// ===== User Parameters =====
interface GitLabGetCurrentUserParams extends GitLabBaseParams {}
interface GitLabListUsersParams extends GitLabBaseParams {
@@ -452,8 +545,6 @@ interface GitLabListUsersParams extends GitLabBaseParams {
page?: number
}
// ===== Response Types =====
export interface GitLabListProjectsResponse extends ToolResponse {
output: {
projects?: GitLabProject[]
@@ -560,7 +651,7 @@ export interface GitLabCancelPipelineResponse extends ToolResponse {
}
}
interface GitLabListBranchesResponse extends ToolResponse {
export interface GitLabListBranchesResponse extends ToolResponse {
output: {
branches?: GitLabBranch[]
total?: number
@@ -573,9 +664,12 @@ interface GitLabGetBranchResponse extends ToolResponse {
}
}
interface GitLabCreateBranchResponse extends ToolResponse {
export interface GitLabCreateBranchResponse extends ToolResponse {
output: {
branch?: GitLabBranch
name?: string | null
webUrl?: string | null
protected?: boolean | null
commit?: GitLabBranch['commit'] | null
}
}
@@ -624,7 +718,83 @@ interface GitLabListUsersResponse extends ToolResponse {
}
}
// ===== Union Response Type =====
export interface GitLabListRepositoryTreeResponse extends ToolResponse {
output: {
tree?: GitLabTreeEntry[]
total?: number
}
}
export interface GitLabGetFileResponse extends ToolResponse {
output: {
filePath?: string | null
fileName?: string | null
size?: number | null
ref?: string | null
blobId?: string | null
lastCommitId?: string | null
content?: string
}
}
export interface GitLabCreateFileResponse extends ToolResponse {
output: {
filePath?: string | null
branch?: string | null
}
}
export interface GitLabUpdateFileResponse extends ToolResponse {
output: {
filePath?: string | null
branch?: string | null
}
}
export interface GitLabListCommitsResponse extends ToolResponse {
output: {
commits?: GitLabCommit[]
total?: number
}
}
export interface GitLabGetMergeRequestChangesResponse extends ToolResponse {
output: {
mergeRequestIid?: number | null
changes?: GitLabMergeRequestChange[]
changesCount?: number
}
}
export interface GitLabApproveMergeRequestResponse extends ToolResponse {
output: {
approvalsRequired?: number | null
approvalsLeft?: number | null
approvedBy?: unknown[]
}
}
export interface GitLabListPipelineJobsResponse extends ToolResponse {
output: {
jobs?: GitLabJob[]
total?: number
}
}
export interface GitLabGetJobLogResponse extends ToolResponse {
output: {
log?: string
}
}
export interface GitLabPlayJobResponse extends ToolResponse {
output: {
id?: number | null
name?: string | null
status?: string | null
webUrl?: string | null
}
}
export type GitLabResponse =
| GitLabListProjectsResponse
@@ -654,3 +824,13 @@ export type GitLabResponse =
| GitLabCreateLabelResponse
| GitLabGetCurrentUserResponse
| GitLabListUsersResponse
| GitLabListRepositoryTreeResponse
| GitLabGetFileResponse
| GitLabCreateFileResponse
| GitLabUpdateFileResponse
| GitLabListCommitsResponse
| GitLabGetMergeRequestChangesResponse
| GitLabApproveMergeRequestResponse
| GitLabListPipelineJobsResponse
| GitLabGetJobLogResponse
| GitLabPlayJobResponse
+118
View File
@@ -0,0 +1,118 @@
import type { GitLabUpdateFileParams, GitLabUpdateFileResponse } from '@/tools/gitlab/types'
import { getGitLabApiBase } from '@/tools/gitlab/utils'
import type { ToolConfig } from '@/tools/types'
export const gitlabUpdateFileTool: ToolConfig<GitLabUpdateFileParams, GitLabUpdateFileResponse> = {
id: 'gitlab_update_file',
name: 'GitLab Update File',
description: 'Update an existing file in a GitLab project repository',
version: '1.0.0',
params: {
accessToken: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'GitLab Personal Access Token',
},
host: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'Self-managed GitLab host (e.g. gitlab.example.com). Defaults to gitlab.com.',
},
projectId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Project ID or URL-encoded path',
},
filePath: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Path to the file in the repository',
},
branch: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Branch to commit the update to',
},
content: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'New file content',
},
commitMessage: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Commit message',
},
lastCommitId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Last known commit ID for the file (optimistic locking)',
},
},
request: {
url: (params) => {
const encodedId = encodeURIComponent(String(params.projectId).trim())
const encodedPath = encodeURIComponent(String(params.filePath))
return `${getGitLabApiBase(params.host)}/projects/${encodedId}/repository/files/${encodedPath}`
},
method: 'PUT',
headers: (params) => ({
'Content-Type': 'application/json',
'PRIVATE-TOKEN': params.accessToken,
}),
body: (params) => {
const body: Record<string, unknown> = {
branch: params.branch,
content: params.content,
commit_message: params.commitMessage,
encoding: 'text',
}
if (params.lastCommitId) body.last_commit_id = params.lastCommitId
return body
},
},
transformResponse: async (response) => {
if (!response.ok) {
const errorText = await response.text()
return {
success: false,
error: `GitLab API error: ${response.status} ${errorText}`,
output: {},
}
}
const data = await response.json()
return {
success: true,
output: {
filePath: data.file_path ?? null,
branch: data.branch ?? null,
},
}
},
outputs: {
filePath: {
type: 'string',
description: 'The updated file path',
},
branch: {
type: 'string',
description: 'The branch the update was committed to',
},
},
}
+1 -1
View File
@@ -86,7 +86,7 @@ export const gitlabUpdateIssueTool: ToolConfig<GitLabUpdateIssueParams, GitLabUp
request: {
url: (params) => {
const encodedId = encodeURIComponent(String(params.projectId))
const encodedId = encodeURIComponent(String(params.projectId).trim())
return `${getGitLabApiBase(params.host)}/projects/${encodedId}/issues/${params.issueIid}`
},
method: 'PUT',
@@ -103,7 +103,7 @@ export const gitlabUpdateMergeRequestTool: ToolConfig<
request: {
url: (params) => {
const encodedId = encodeURIComponent(String(params.projectId))
const encodedId = encodeURIComponent(String(params.projectId).trim())
return `${getGitLabApiBase(params.host)}/projects/${encodedId}/merge_requests/${params.mergeRequestIid}`
},
method: 'PUT',
+42 -18
View File
@@ -1082,23 +1082,35 @@ import {
githubUpdateReleaseV2Tool,
} from '@/tools/github'
import {
gitlabApproveMergeRequestTool,
gitlabCancelPipelineTool,
gitlabCreateBranchTool,
gitlabCreateFileTool,
gitlabCreateIssueNoteTool,
gitlabCreateIssueTool,
gitlabCreateMergeRequestNoteTool,
gitlabCreateMergeRequestTool,
gitlabCreatePipelineTool,
gitlabDeleteIssueTool,
gitlabGetFileTool,
gitlabGetIssueTool,
gitlabGetJobLogTool,
gitlabGetMergeRequestChangesTool,
gitlabGetMergeRequestTool,
gitlabGetPipelineTool,
gitlabGetProjectTool,
gitlabListBranchesTool,
gitlabListCommitsTool,
gitlabListIssuesTool,
gitlabListMergeRequestsTool,
gitlabListPipelineJobsTool,
gitlabListPipelinesTool,
gitlabListProjectsTool,
gitlabListRepositoryTreeTool,
gitlabMergeMergeRequestTool,
gitlabPlayJobTool,
gitlabRetryPipelineTool,
gitlabUpdateFileTool,
gitlabUpdateIssueTool,
gitlabUpdateMergeRequestTool,
} from '@/tools/gitlab'
@@ -5477,25 +5489,37 @@ export const tools: Record<string, ToolConfig> = {
github_check_star_v2: githubCheckStarV2Tool,
github_list_stargazers: githubListStargazersTool,
github_list_stargazers_v2: githubListStargazersV2Tool,
gitlab_list_projects: gitlabListProjectsTool,
gitlab_get_project: gitlabGetProjectTool,
gitlab_list_issues: gitlabListIssuesTool,
gitlab_get_issue: gitlabGetIssueTool,
gitlab_create_issue: gitlabCreateIssueTool,
gitlab_update_issue: gitlabUpdateIssueTool,
gitlab_delete_issue: gitlabDeleteIssueTool,
gitlab_create_issue_note: gitlabCreateIssueNoteTool,
gitlab_list_merge_requests: gitlabListMergeRequestsTool,
gitlab_get_merge_request: gitlabGetMergeRequestTool,
gitlab_create_merge_request: gitlabCreateMergeRequestTool,
gitlab_update_merge_request: gitlabUpdateMergeRequestTool,
gitlab_merge_merge_request: gitlabMergeMergeRequestTool,
gitlab_create_merge_request_note: gitlabCreateMergeRequestNoteTool,
gitlab_list_pipelines: gitlabListPipelinesTool,
gitlab_get_pipeline: gitlabGetPipelineTool,
gitlab_create_pipeline: gitlabCreatePipelineTool,
gitlab_retry_pipeline: gitlabRetryPipelineTool,
gitlab_approve_merge_request: gitlabApproveMergeRequestTool,
gitlab_cancel_pipeline: gitlabCancelPipelineTool,
gitlab_create_branch: gitlabCreateBranchTool,
gitlab_create_file: gitlabCreateFileTool,
gitlab_create_issue: gitlabCreateIssueTool,
gitlab_create_issue_note: gitlabCreateIssueNoteTool,
gitlab_create_merge_request: gitlabCreateMergeRequestTool,
gitlab_create_merge_request_note: gitlabCreateMergeRequestNoteTool,
gitlab_create_pipeline: gitlabCreatePipelineTool,
gitlab_delete_issue: gitlabDeleteIssueTool,
gitlab_get_file: gitlabGetFileTool,
gitlab_get_issue: gitlabGetIssueTool,
gitlab_get_job_log: gitlabGetJobLogTool,
gitlab_get_merge_request: gitlabGetMergeRequestTool,
gitlab_get_merge_request_changes: gitlabGetMergeRequestChangesTool,
gitlab_get_pipeline: gitlabGetPipelineTool,
gitlab_get_project: gitlabGetProjectTool,
gitlab_list_branches: gitlabListBranchesTool,
gitlab_list_commits: gitlabListCommitsTool,
gitlab_list_issues: gitlabListIssuesTool,
gitlab_list_merge_requests: gitlabListMergeRequestsTool,
gitlab_list_pipeline_jobs: gitlabListPipelineJobsTool,
gitlab_list_pipelines: gitlabListPipelinesTool,
gitlab_list_projects: gitlabListProjectsTool,
gitlab_list_repository_tree: gitlabListRepositoryTreeTool,
gitlab_merge_merge_request: gitlabMergeMergeRequestTool,
gitlab_play_job: gitlabPlayJobTool,
gitlab_retry_pipeline: gitlabRetryPipelineTool,
gitlab_update_file: gitlabUpdateFileTool,
gitlab_update_issue: gitlabUpdateIssueTool,
gitlab_update_merge_request: gitlabUpdateMergeRequestTool,
grain_list_recordings: grainListRecordingsTool,
grain_get_recording: grainGetRecordingTool,
grain_get_transcript: grainGetTranscriptTool,