diff --git a/README.md b/README.md index 98158f60..2d811a75 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,8 @@ Some MCP clients (like GitHub Copilot CLI) have issues with environment variable - `--token` - GitLab Personal Access Token (replaces `GITLAB_PERSONAL_ACCESS_TOKEN`) - `--api-url` - GitLab API URL (replaces `GITLAB_API_URL`) - `--read-only=true` - Enable read-only mode (replaces `GITLAB_READ_ONLY_MODE`) +- `--toolsets=all` - Enable named toolsets (replaces `GITLAB_TOOLSETS`; unset uses the lean `core` default) +- `--tools=list_issues` - Add individual tools (replaces `GITLAB_TOOLS`) - `--use-wiki=true` - Enable wiki API (replaces `USE_GITLAB_WIKI`) - `--use-milestone=true` - Enable milestone API (replaces `USE_MILESTONE`) - `--use-pipeline=true` - Enable pipeline API (replaces `USE_PIPELINE`) diff --git a/docs/configuration/environment-variables.md b/docs/configuration/environment-variables.md index 4b33f9e2..dae43538 100644 --- a/docs/configuration/environment-variables.md +++ b/docs/configuration/environment-variables.md @@ -322,14 +322,15 @@ Set to `true` to expose only read-only tools. ### `GITLAB_TOOLSETS` -Comma-separated list of toolset IDs to enable. +Comma-separated list of toolset IDs to enable. If unset, the server exposes the lean `core` toolset plus the always-on `discover_tools` meta-tool. For agent-specific tool surfaces, see [Custom Agents and Multiple PAT Setup](../auth/custom-agent-multiple-pat.md). -Special value: +Special values: -- `all` +- `all` — enable every toolset. +- `merge_requests,issues,repositories,branches,projects,labels,ci,groups,users` — restore the pre-lean default set. ### `GITLAB_TOOLS` diff --git a/docs/getting-started/cli-arguments.md b/docs/getting-started/cli-arguments.md index 32d5a6ed..d6ca6667 100644 --- a/docs/getting-started/cli-arguments.md +++ b/docs/getting-started/cli-arguments.md @@ -32,6 +32,8 @@ CLI arguments take precedence over environment variables. | `--token` | `GITLAB_PERSONAL_ACCESS_TOKEN` | GitLab Personal Access Token. | | `--api-url` | `GITLAB_API_URL` | GitLab API URL (e.g., `https://gitlab.com/api/v4`). | | `--read-only=true` | `GITLAB_READ_ONLY_MODE` | Enable read-only mode. | +| `--toolsets=all` | `GITLAB_TOOLSETS` | Enable named toolsets. Unset uses lean `core`. | +| `--tools=list_issues` | `GITLAB_TOOLS` | Add individual tools on top of enabled toolsets. | | `--use-wiki=true` | `USE_GITLAB_WIKI` | Enable wiki API tools. | | `--use-milestone=true` | `USE_MILESTONE` | Enable milestone API tools. | | `--use-pipeline=true` | `USE_PIPELINE` | Enable pipeline API tools. | diff --git a/docs/tools/branches.md b/docs/tools/branches.md index e973d059..606474f7 100644 --- a/docs/tools/branches.md +++ b/docs/tools/branches.md @@ -2,6 +2,9 @@ Branch management, commit listing/inspection, file blame, and CI commit-status manipulation. +!!! note "Feature toggle" + Opt-in. Enable via `GITLAB_TOOLSETS=branches` (or `GITLAB_TOOLSETS=all`), list individual tools in `GITLAB_TOOLS=`, or activate at runtime with the `discover_tools` MCP tool. + ## Tools in this group - [`create_branch`](#create_branch) — ✏️ Writes diff --git a/docs/tools/ci.md b/docs/tools/ci.md index c12fe22e..7d2ebe23 100644 --- a/docs/tools/ci.md +++ b/docs/tools/ci.md @@ -2,6 +2,9 @@ Validate `.gitlab-ci.yml` snippets and project pipeline configs. +!!! note "Feature toggle" + Opt-in. Enable via `GITLAB_TOOLSETS=ci` (or `GITLAB_TOOLSETS=all`), list individual tools in `GITLAB_TOOLS=`, or activate at runtime with the `discover_tools` MCP tool. + ## Tools in this group - [`validate_ci_lint`](#validate_ci_lint) — 📖 Read-only diff --git a/docs/tools/core.md b/docs/tools/core.md new file mode 100644 index 00000000..0620de7d --- /dev/null +++ b/docs/tools/core.md @@ -0,0 +1,660 @@ +# Core + +Lean default starter set for common MR, issue, repository, branch, project, label, and identity workflows. + +## Tools in this group + +- [`list_merge_requests`](#list_merge_requests) — 📖 Read-only +- [`get_merge_request`](#get_merge_request) — 📖 Read-only +- [`get_merge_request_approval_state`](#get_merge_request_approval_state) — 📖 Read-only +- [`list_merge_request_changed_files`](#list_merge_request_changed_files) — 📖 Read-only +- [`get_merge_request_file_diff`](#get_merge_request_file_diff) — 📖 Read-only +- [`list_merge_request_diffs`](#list_merge_request_diffs) — 📖 Read-only +- [`get_merge_request_diffs`](#get_merge_request_diffs) — 📖 Read-only +- [`mr_discussions`](#mr_discussions) — 📖 Read-only +- [`create_merge_request`](#create_merge_request) — ✏️ Writes +- [`create_merge_request_thread`](#create_merge_request_thread) — ✏️ Writes +- [`resolve_merge_request_thread`](#resolve_merge_request_thread) — ✏️ Writes +- [`update_merge_request`](#update_merge_request) — ✏️ Writes +- [`list_issues`](#list_issues) — 📖 Read-only +- [`my_issues`](#my_issues) — 📖 Read-only +- [`get_issue`](#get_issue) — 📖 Read-only +- [`create_issue`](#create_issue) — ✏️ Writes +- [`update_issue`](#update_issue) — ✏️ Writes +- [`create_issue_note`](#create_issue_note) — ✏️ Writes +- [`list_issue_discussions`](#list_issue_discussions) — 📖 Read-only +- [`update_issue_description_patch`](#update_issue_description_patch) — ✏️ Writes +- [`get_file_contents`](#get_file_contents) — 📖 Read-only +- [`get_repository_tree`](#get_repository_tree) — 📖 Read-only +- [`search_repositories`](#search_repositories) — 📖 Read-only +- [`get_branch`](#get_branch) — 📖 Read-only +- [`list_branches`](#list_branches) — 📖 Read-only +- [`list_commits`](#list_commits) — 📖 Read-only +- [`get_commit`](#get_commit) — 📖 Read-only +- [`get_commit_diff`](#get_commit_diff) — 📖 Read-only +- [`get_file_blame`](#get_file_blame) — 📖 Read-only +- [`get_project`](#get_project) — 📖 Read-only +- [`list_projects`](#list_projects) — 📖 Read-only +- [`list_project_members`](#list_project_members) — 📖 Read-only +- [`list_labels`](#list_labels) — 📖 Read-only +- [`whoami`](#whoami) — 📖 Read-only +- [`health_check`](#health_check) — 📖 Read-only + +--- + +### `list_merge_requests` + +*📖 Read-only* + +List merge requests (without project_id: user's MRs; with project_id: project MRs) + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `project_id` | string | | Project ID or URL-encoded path (optional - if not provided, lists all merge requests the user has access to) | +| `assignee_id` | string | | Return MRs assigned to the given user ID (integer), 'none', or 'any'. Mutually exclusive with assignee_username. | +| `assignee_username` | string | | Returns merge requests assigned to the given username. Mutually exclusive with assignee_id. | +| `author_id` | string | | Returns merge requests created by the given user ID (integer). Mutually exclusive with author_username. | +| `author_username` | string | | Returns merge requests created by the given username. Mutually exclusive with author_id. | +| `reviewer_id` | string | | Returns merge requests which have the user as a reviewer. Must be an integer, 'none', or 'any'. Mutually exclusive with reviewer_username. | +| `reviewer_username` | string | | Returns merge requests which have the user as a reviewer by username. Mutually exclusive with reviewer_id. | +| `approved_by_usernames` | array | | Returns merge requests approved by the given usernames (array). | +| `created_after` | string | | Return merge requests created after the given time | +| `created_before` | string | | Return merge requests created before the given time | +| `updated_after` | string | | Return merge requests updated after the given time | +| `updated_before` | string | | Return merge requests updated before the given time | +| `labels` | array | | Array of label names | +| `milestone` | string | | Milestone title | +| `scope` | enum (`created_by_me` \| `assigned_to_me` \| `all`) | | Return merge requests from a specific scope | +| `search` | string | | Search for specific terms | +| `state` | enum (`opened` \| `closed` \| `locked` \| `merged` \| `all`) | | Return merge requests with a specific state | +| `order_by` | enum (`created_at` \| `updated_at` \| `priority` \| `label_priority` \| `milestone_due` \| `popularity`) | | Return merge requests ordered by the given field | +| `sort` | enum (`asc` \| `desc`) | | Return merge requests sorted in ascending or descending order | +| `target_branch` | string | | Return merge requests targeting a specific branch | +| `source_branch` | string | | Return merge requests from a specific source branch | +| `wip` | enum (`yes` \| `no`) | | Filter merge requests against their wip status | +| `with_labels_details` | boolean | | Return more details for each label | +| `page` | number | | Page number for pagination (default: 1) | +| `per_page` | number | | Number of items per page (max: 100, default: 20) | + +### `get_merge_request` + +*📖 Read-only* + +Get details of a merge request (mergeRequestIid or branchName required) + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `project_id` | string | ✓ | Project ID or complete URL-encoded path to project | +| `merge_request_iid` | string | | The IID of a merge request | +| `source_branch` | string | | Source branch name | + +### `get_merge_request_approval_state` + +*📖 Read-only* + +Get merge request approval details including approvers + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `project_id` | string | ✓ | Project ID or complete URL-encoded path to project | +| `merge_request_iid` | string | ✓ | The IID of the merge request | + +### `list_merge_request_changed_files` + +*📖 Read-only* + +List changed file paths in a merge request without diff content (mergeRequestIid or branchName required) + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `project_id` | string | ✓ | Project ID or complete URL-encoded path to project | +| `merge_request_iid` | string | | The IID of a merge request | +| `source_branch` | string | | Source branch name | +| `excluded_file_patterns` | array | | Array of regex patterns to exclude files. Examples: ["^vendor/", "\.pb\.go$"] | + +### `get_merge_request_file_diff` + +*📖 Read-only* + +Get diffs for specific files from a merge request (mergeRequestIid or branchName required) + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `project_id` | string | ✓ | Project ID or complete URL-encoded path to project | +| `merge_request_iid` | string | | The IID of a merge request | +| `source_branch` | string | | Source branch name | +| `file_paths` | array | ✓ | List of file paths to retrieve diffs for (e.g. ['src/api/users.ts', 'src/repo/user.go']). Call list_merge_request_changed_files first to get the full list of changed paths. | +| `unidiff` | boolean | | Present diff in the unified diff format. Default is false. | + +### `list_merge_request_diffs` + +*📖 Read-only* + +List merge request diffs with pagination (mergeRequestIid or branchName required) + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `project_id` | string | ✓ | Project ID or complete URL-encoded path to project | +| `merge_request_iid` | string | | The IID of a merge request | +| `source_branch` | string | | Source branch name | +| `page` | number | | Page number for pagination (default: 1) | +| `per_page` | number | | Number of items per page (max: 100, default: 20) | +| `unidiff` | boolean | | Present diffs in the unified diff format. Default is false. Introduced in GitLab 16.5. | + +### `get_merge_request_diffs` + +*📖 Read-only* + +Get the changes/diffs of a merge request (mergeRequestIid or branchName required) + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `project_id` | string | ✓ | Project ID or complete URL-encoded path to project | +| `merge_request_iid` | string | | The IID of a merge request | +| `source_branch` | string | | Source branch name | +| `view` | enum (`inline` \| `parallel`) | | Diff view type | +| `excluded_file_patterns` | array | | Array of regex patterns to exclude files from the diff results. Each pattern is a JavaScript-compatible regular expression that matches file paths to ignore. Examples: ["^vendor/", "^test/mocks/", "\.spec\.ts$", "package-lock\.json"] | + +### `mr_discussions` + +*📖 Read-only* + +List discussion items for a merge request + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `project_id` | string | ✓ | Project ID or complete URL-encoded path to project | +| `merge_request_iid` | string | ✓ | The IID of a merge request | +| `page` | number | | Page number for pagination (default: 1) | +| `per_page` | number | | Number of items per page (max: 100, default: 20) | + +### `create_merge_request` + +*✏️ Writes* + +Create a new merge request + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `project_id` | string | ✓ | Project ID or complete URL-encoded path to project | +| `title` | string | ✓ | Merge request title | +| `description` | string | | Merge request description | +| `source_branch` | string | ✓ | Branch containing changes | +| `target_branch` | string | ✓ | Branch to merge into | +| `target_project_id` | string | | Numeric ID of the target project. | +| `assignee_ids` | array | | The ID of the users to assign the MR to | +| `reviewer_ids` | array | | The ID of the users to assign as reviewers of the MR | +| `labels` | array | | Labels for the MR | +| `draft` | boolean | | Create as draft merge request | +| `allow_collaboration` | boolean | | Allow commits from upstream members | +| `remove_source_branch` | boolean \| null | | Flag indicating if a merge request should remove the source branch when merging. | +| `squash` | boolean \| null | | If true, squash all commits into a single commit on merge. | + +### `create_merge_request_thread` + +*✏️ Writes* + +Create a new thread on a merge request + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `project_id` | string | ✓ | Project ID or complete URL-encoded path to project | +| `merge_request_iid` | string | ✓ | The IID of a merge request | +| `body` | string | ✓ | The content of the thread | +| `position` | object | | Position when creating a diff note | +| `created_at` | string | | Date the thread was created at (ISO 8601 format) | + +### `resolve_merge_request_thread` + +*✏️ Writes* + +Resolve a thread on a merge request + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `project_id` | string | ✓ | Project ID or complete URL-encoded path to project | +| `merge_request_iid` | string | ✓ | The IID of a merge request | +| `discussion_id` | string | ✓ | The ID of a thread | +| `resolved` | boolean | ✓ | Whether to resolve the thread | + +### `update_merge_request` + +*✏️ Writes* + +Update a merge request (mergeRequestIid or branchName required) + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `project_id` | string | ✓ | Project ID or complete URL-encoded path to project | +| `merge_request_iid` | string | | The IID of a merge request | +| `source_branch` | string | | Source branch name | +| `title` | string | | The title of the merge request | +| `description` | string | | The description of the merge request | +| `target_branch` | string | | The target branch | +| `assignee_ids` | array | | The ID of the users to assign the MR to | +| `reviewer_ids` | array | | The ID of the users to assign as reviewers of the MR | +| `labels` | array | | Labels for the MR | +| `state_event` | enum (`close` \| `reopen`) | | New state (close/reopen) for the MR | +| `remove_source_branch` | boolean | | Flag indicating if the source branch should be removed | +| `squash` | boolean | | Squash commits into a single commit when merging | +| `draft` | boolean | | Work in progress merge request | + +### `list_issues` + +*📖 Read-only* + +List issues (default: created by current user; use scope='all' for all) + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `project_id` | string | | Project ID or URL-encoded path (optional - if not provided, lists issues across all accessible projects) | +| `assignee_id` | string | | Return issues assigned to the given user ID (user id, none, or any). Mutually exclusive with assignee_username. | +| `assignee_username` | array | | Return issues assigned to the given username. Mutually exclusive with assignee_id. | +| `author_id` | string | | Return issues created by the given user ID. Mutually exclusive with author_username. | +| `author_username` | string | | Return issues created by the given username. Mutually exclusive with author_id. | +| `confidential` | boolean | | Filter confidential or public issues | +| `created_after` | string | | Return issues created after the given time | +| `created_before` | string | | Return issues created before the given time | +| `due_date` | string | | Return issues that have the due date | +| `labels` | array | | Array of label names | +| `milestone` | string | | Milestone title | +| `issue_type` | enum (`issue` \| `incident` \| `test_case` \| `task`) | | Filter to a given type of issue. One of issue, incident, test_case or task | +| `iteration_id` | string | | Return issues assigned to the given iteration ID. None returns issues that do not belong to an iteration. Any returns issues that belong to an iteration. | +| `scope` | enum (`created_by_me` \| `assigned_to_me` \| `all`) | | Return issues from a specific scope | +| `search` | string | | Search for specific terms | +| `state` | enum (`opened` \| `closed` \| `all`) | | Return issues with a specific state | +| `updated_after` | string | | Return issues updated after the given time | +| `updated_before` | string | | Return issues updated before the given time | +| `with_labels_details` | boolean | | Return more details for each label | +| `page` | number | | Page number for pagination (default: 1) | +| `per_page` | number | | Number of items per page (max: 100, default: 20) | + +### `my_issues` + +*📖 Read-only* + +List issues assigned to the authenticated user + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `project_id` | string | | Project ID or URL-encoded path (optional to search across all accessible projects) | +| `state` | enum (`opened` \| `closed` \| `all`) | | Return issues with a specific state (default: opened) | +| `labels` | array | | Array of label names to filter by | +| `milestone` | string | | Milestone title to filter by | +| `search` | string | | Search for specific terms in title and description | +| `created_after` | string | | Return issues created after the given time (ISO 8601) | +| `created_before` | string | | Return issues created before the given time (ISO 8601) | +| `updated_after` | string | | Return issues updated after the given time (ISO 8601) | +| `updated_before` | string | | Return issues updated before the given time (ISO 8601) | +| `per_page` | number | | Number of items per page (default: 20, max: 100) | +| `page` | number | | Page number for pagination (default: 1) | + +### `get_issue` + +*📖 Read-only* + +Get details of a specific issue + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `project_id` | string | ✓ | Project ID or URL-encoded path | +| `issue_iid` | string | ✓ | The internal ID of the project issue | + +### `create_issue` + +*✏️ Writes* + +Create a new issue + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `project_id` | string | ✓ | Project ID or complete URL-encoded path to project | +| `title` | string | ✓ | Issue title | +| `description` | string | | Issue description | +| `assignee_ids` | array | | Array of user IDs to assign | +| `labels` | array | | Array of label names | +| `milestone_id` | string | | Milestone ID to assign | +| `issue_type` | enum (`issue` \| `incident` \| `test_case` \| `task`) | | The type of issue. One of issue, incident, test_case or task. | +| `weight` | number | | Weight of the issue (numeric, typically hours of work) | + +### `update_issue` + +*✏️ Writes* + +Update an issue + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `project_id` | string | ✓ | Project ID or URL-encoded path | +| `issue_iid` | string | ✓ | The internal ID of the project issue | +| `title` | string | | The title of the issue | +| `description` | string | | The description of the issue | +| `assignee_ids` | array | | Array of user IDs to assign issue to | +| `confidential` | boolean | | Set the issue to be confidential | +| `discussion_locked` | boolean | | Flag to lock discussions | +| `due_date` | string | | Date the issue is due (YYYY-MM-DD) | +| `labels` | array | | Array of label names | +| `milestone_id` | string | | Milestone ID to assign | +| `state_event` | enum (`close` \| `reopen`) | | Update issue state (close/reopen) | +| `weight` | number | | Weight of the issue (numeric, typically hours of work) | +| `issue_type` | enum (`issue` \| `incident` \| `test_case` \| `task`) | | The type of issue. One of issue, incident, test_case or task. | + +### `create_issue_note` + +*✏️ Writes* + +Add a note to an issue, optionally replying to a discussion thread + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `project_id` | string | ✓ | Project ID or complete URL-encoded path to project | +| `issue_iid` | string | ✓ | The IID of an issue | +| `discussion_id` | string | | The ID of a thread. If provided, replies to that thread; otherwise creates a top-level note | +| `body` | string | ✓ | The content of the note or reply | +| `created_at` | string | | Date the note was created at (ISO 8601 format) | + +### `list_issue_discussions` + +*📖 Read-only* + +List discussions for an issue + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `project_id` | string | ✓ | Project ID or URL-encoded path | +| `issue_iid` | string | ✓ | The internal ID of the project issue | +| `page` | number | | Page number for pagination (default: 1) | +| `per_page` | number | | Number of items per page (max: 100, default: 20) | + +### `update_issue_description_patch` + +*✏️ Writes* + +Apply a patch (search/replace or unified diff) to an issue description. Reduces token usage by allowing small changes without sending the full description. Supports dry_run to preview changes and create_note to summarize updates. + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `project_id` | string | ✓ | Project ID or URL-encoded path | +| `issue_iid` | string | ✓ | The internal ID of the project issue | +| `patch_type` | enum (`search_replace` \| `unified_diff`) | ✓ | Type of patch format to apply | +| `patch` | string | ✓ | The patch content to apply to the issue description | +| `dry_run` | boolean | | If true, preview changes without updating the issue | +| `create_note` | boolean | | If true, add a note summarizing the change after update | +| `allow_multiple` | boolean | | For search_replace: allow multiple matches to all be replaced (default: false — fail on duplicate) | + +### `get_file_contents` + +*📖 Read-only* + +Get contents of a file or directory from a GitLab project + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `project_id` | string | | Project ID or URL-encoded path (optional; falls back to env) | +| `file_path` | string | | Path to the file or directory. Takes precedence over 'path' when both are provided | +| `path` | string | | Alias of file_path | +| `ref` | string | | Branch/tag/commit to get contents from | + +### `get_repository_tree` + +*📖 Read-only* + +List files and directories in a repository + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `project_id` | string | ✓ | The ID or URL-encoded path of the project | +| `path` | string | | The path inside the repository | +| `ref` | string | | The name of a repository branch or tag. Defaults to the default branch. | +| `recursive` | boolean | | Boolean value to get a recursive tree | +| `per_page` | number | | Number of results to show per page | +| `page_token` | string | | Token for keyset pagination. Use the next_page_token value returned in the previous response to retrieve the next page. | +| `pagination` | string | | Pagination method. Use 'keyset' for keyset-based pagination (required for repositories with many files). Non-keyset calls keep the legacy array response for backward compatibility; that legacy response shape is deprecated and may be removed in a future major release. Keyset calls return a structured response with items and next_page_token when more pages are available. | + +### `search_repositories` + +*📖 Read-only* + +Search for GitLab projects + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `search` | string | | Search query | +| `query` | string | | Search query (alias for 'search') | +| `page` | number | | Page number for pagination (default: 1) | +| `per_page` | number | | Number of items per page (max: 100, default: 20) | + +### `get_branch` + +*📖 Read-only* + +Get branch details (commit, protection status) + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `project_id` | string | ✓ | Project ID or complete URL-encoded path to project | +| `branch_name` | string | ✓ | Name of the branch | + +### `list_branches` + +*📖 Read-only* + +List branches in project with search filter + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `project_id` | string | ✓ | Project ID or complete URL-encoded path to project | +| `search` | string | | Search term to filter branches by name | +| `page` | number | | Page number for pagination (default: 1) | +| `per_page` | number | | Number of items per page (max: 100, default: 20) | + +### `list_commits` + +*📖 Read-only* + +List repository commits with filtering options + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `project_id` | string | ✓ | Project ID or complete URL-encoded path to project | +| `ref_name` | string | | The name of a repository branch, tag or revision range, or if not given the default branch | +| `since` | string | | Only commits after or on this date are returned in ISO 8601 format YYYY-MM-DDTHH:MM:SSZ | +| `until` | string | | Only commits before or on this date are returned in ISO 8601 format YYYY-MM-DDTHH:MM:SSZ | +| `path` | string | | The file path | +| `author` | string | | Search commits by commit author | +| `all` | boolean | | Retrieve every commit from the repository | +| `with_stats` | boolean | | Stats about each commit are added to the response | +| `first_parent` | boolean | | Follow only the first parent commit upon seeing a merge commit | +| `order` | enum (`default` \| `topo`) | | List commits in order | +| `trailers` | boolean | | Parse and include Git trailers for every commit | +| `page` | number | | Page number for pagination (default: 1) | +| `per_page` | number | | Number of items per page (max: 100, default: 20) | + +### `get_commit` + +*📖 Read-only* + +Get details of a specific commit + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `project_id` | string | ✓ | Project ID or complete URL-encoded path to project | +| `sha` | string | ✓ | The commit hash or name of a repository branch or tag | +| `stats` | boolean | | Include commit stats | + +### `get_commit_diff` + +*📖 Read-only* + +Get changes/diffs of a specific commit + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `project_id` | string | ✓ | Project ID or complete URL-encoded path to project | +| `sha` | string | ✓ | The commit hash or name of a repository branch or tag | +| `full_diff` | boolean | | Whether to return the full diff or only first page (default: false) | + +### `get_file_blame` + +*📖 Read-only* + +Get git blame for a file at a given ref. Each entry maps a contiguous range of source lines to the commit that last changed them (id, author, authored_date, message). Use range_start/range_end to limit blame to specific lines. + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `project_id` | string | | Project ID or complete URL-encoded path to project | +| `file_path` | string | ✓ | The full path of the file to blame, relative to repo root | +| `ref` | string | ✓ | The name of branch, tag or commit (required by GitLab blame API) | +| `range_start` | integer | | First line of the blame range (inclusive, 1-based). Both range[start] and range[end] must be set together. | +| `range_end` | integer | | Last line of the blame range (inclusive, 1-based). Both range[start] and range[end] must be set together. | + +### `get_project` + +*📖 Read-only* + +Get details of a specific project + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `project_id` | string | ✓ | Project ID or URL-encoded path | + +### `list_projects` + +*📖 Read-only* + +List projects accessible by the current user + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `search` | string | | Search term for projects | +| `search_namespaces` | boolean | | Needs to be true if search is full path | +| `owned` | boolean | | Filter for projects owned by current user | +| `membership` | boolean | | Filter for projects where current user is a member | +| `simple` | boolean | | Return only limited fields | +| `archived` | boolean | | Filter for archived projects | +| `visibility` | enum (`public` \| `internal` \| `private`) | | Filter by project visibility | +| `order_by` | enum (`id` \| `name` \| `path` \| `created_at` \| `updated_at` \| `last_activity_at`) | | Return projects ordered by field | +| `sort` | enum (`asc` \| `desc`) | | Return projects sorted in ascending or descending order | +| `with_issues_enabled` | boolean | | Filter projects with issues feature enabled | +| `with_merge_requests_enabled` | boolean | | Filter projects with merge requests feature enabled | +| `min_access_level` | number | | Filter by minimum access level | +| `topic` | string | | Filter by topic (projects tagged with this topic) | +| `page` | number | | Page number for pagination (default: 1) | +| `per_page` | number | | Number of items per page (max: 100, default: 20) | + +### `list_project_members` + +*📖 Read-only* + +List members of a GitLab project + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `project_id` | string | ✓ | Project ID or URL-encoded path | +| `query` | string | | Search for members by name or username | +| `user_ids` | array | | Filter by user IDs | +| `skip_users` | array | | User IDs to exclude | +| `include_inheritance` | boolean | | Include inherited members. Defaults to false. | +| `per_page` | number | | Number of items per page (default: 20, max: 100) | +| `page` | number | | Page number for pagination (default: 1) | + +### `list_labels` + +*📖 Read-only* + +List labels for a project + +**Parameters** + +| Parameter | Type | Required | Description | +|---|---|:-:|---| +| `project_id` | string | ✓ | Project ID or URL-encoded path | +| `with_counts` | boolean | | Whether to include issue and merge request counts | +| `include_ancestor_groups` | boolean | | Include ancestor groups | +| `search` | string | | Keyword to filter labels by | +| `page` | number | | Page number for pagination (default: 1) | +| `per_page` | number | | Number of items per page (max: 100, default: 20) | + +### `whoami` + +*📖 Read-only* + +Get current authenticated user details + +**Parameters** + +_No parameters._ + +### `health_check` + +*📖 Read-only* + +Verify server status and authentication + +**Parameters** + +_No parameters._ diff --git a/docs/tools/groups.md b/docs/tools/groups.md index 2beba0ce..ca3bc69e 100644 --- a/docs/tools/groups.md +++ b/docs/tools/groups.md @@ -2,6 +2,9 @@ Create new groups and subgroups. +!!! note "Feature toggle" + Opt-in. Enable via `GITLAB_TOOLSETS=groups` (or `GITLAB_TOOLSETS=all`), list individual tools in `GITLAB_TOOLS=`, or activate at runtime with the `discover_tools` MCP tool. + ## Tools in this group - [`create_group`](#create_group) — ✏️ Writes diff --git a/docs/tools/index.md b/docs/tools/index.md index 58b16d9a..aa8ed552 100644 --- a/docs/tools/index.md +++ b/docs/tools/index.md @@ -16,13 +16,14 @@ directly from `TOOLSET_DEFINITIONS` in | Status | Groups | |---|---| -| **Default** — always exposed | [Projects & Namespaces](projects.md), [Projects & Files](repositories.md), [Branches & Commits](branches.md), [Groups](groups.md), [Merge Requests](merge-requests.md), [Issues](issues.md), [Labels](labels.md), [CI Lint](ci.md), [Users & Events](users.md) | -| **Opt-in** — must be enabled | [Work Items](workitems.md), [Pipelines, Jobs & Deployments](pipelines.md) (also `USE_PIPELINE=true`), [Milestones](milestones.md) (also `USE_MILESTONE=true`), [Wiki](wiki.md) (also `USE_GITLAB_WIKI=true`), [Releases](releases.md), [Tags](tags.md), [Variables](variables.md), [Webhooks](webhooks.md), [Search](search.md), [Dependency Proxy](dependency-proxy.md), [Meta & GraphQL](meta.md) | +| **Default** — always exposed | [Core](core.md) | +| **Opt-in** — must be enabled | [Projects & Namespaces](projects.md), [Projects & Files](repositories.md), [Branches & Commits](branches.md), [Groups](groups.md), [Merge Requests](merge-requests.md), [Issues](issues.md), [Labels](labels.md), [Work Items](workitems.md), [CI Lint](ci.md), [Pipelines, Jobs & Deployments](pipelines.md) (also `USE_PIPELINE=true`), [Milestones](milestones.md) (also `USE_MILESTONE=true`), [Wiki](wiki.md) (also `USE_GITLAB_WIKI=true`), [Releases](releases.md), [Tags](tags.md), [Users & Events](users.md), [Variables](variables.md), [Webhooks](webhooks.md), [Search](search.md), [Dependency Proxy](dependency-proxy.md), [Meta & GraphQL](meta.md) | **How to enable opt-in groups** (any one is sufficient): - `GITLAB_TOOLSETS=` — comma-separated toolset IDs. - `GITLAB_TOOLSETS=all` — enables every group. +- `GITLAB_TOOLSETS=merge_requests,issues,repositories,branches,projects,labels,ci,groups,users` — restores the pre-lean default set. - `GITLAB_TOOLS=` — enables individual tools regardless of group. - `USE_PIPELINE=true` / `USE_MILESTONE=true` / `USE_GITLAB_WIKI=true` — legacy single-group flags (Pipelines, Milestones, Wiki only). - Call the `discover_tools` MCP tool at runtime to activate categories for the current session. @@ -42,10 +43,54 @@ and [CLI Arguments](../getting-started/cli-arguments.md) for the full list. Each group has its own page with full parameter tables — click any tool name to jump to its details, or click the group title for the per-group view. +### [Core](core.md) + +Lean default starter set for common MR, issue, repository, branch, project, label, and identity workflows. *(35 tools)* + +| Tool | What it does | R/W | +|---|---|:-:| +| [`list_merge_requests`](core.md#list_merge_requests) | List merge requests (without project_id: user's MRs; with project_id: project MRs) | 📖 | +| [`get_merge_request`](core.md#get_merge_request) | Get details of a merge request (mergeRequestIid or branchName required) | 📖 | +| [`get_merge_request_approval_state`](core.md#get_merge_request_approval_state) | Get merge request approval details including approvers | 📖 | +| [`list_merge_request_changed_files`](core.md#list_merge_request_changed_files) | List changed file paths in a merge request without diff content (mergeRequestIid or branchName required) | 📖 | +| [`get_merge_request_file_diff`](core.md#get_merge_request_file_diff) | Get diffs for specific files from a merge request (mergeRequestIid or branchName required) | 📖 | +| [`list_merge_request_diffs`](core.md#list_merge_request_diffs) | List merge request diffs with pagination (mergeRequestIid or branchName required) | 📖 | +| [`get_merge_request_diffs`](core.md#get_merge_request_diffs) | Get the changes/diffs of a merge request (mergeRequestIid or branchName required) | 📖 | +| [`mr_discussions`](core.md#mr_discussions) | List discussion items for a merge request | 📖 | +| [`create_merge_request`](core.md#create_merge_request) | Create a new merge request | ✏️ | +| [`create_merge_request_thread`](core.md#create_merge_request_thread) | Create a new thread on a merge request | ✏️ | +| [`resolve_merge_request_thread`](core.md#resolve_merge_request_thread) | Resolve a thread on a merge request | ✏️ | +| [`update_merge_request`](core.md#update_merge_request) | Update a merge request (mergeRequestIid or branchName required) | ✏️ | +| [`list_issues`](core.md#list_issues) | List issues (default: created by current user; use scope='all' for all) | 📖 | +| [`my_issues`](core.md#my_issues) | List issues assigned to the authenticated user | 📖 | +| [`get_issue`](core.md#get_issue) | Get details of a specific issue | 📖 | +| [`create_issue`](core.md#create_issue) | Create a new issue | ✏️ | +| [`update_issue`](core.md#update_issue) | Update an issue | ✏️ | +| [`create_issue_note`](core.md#create_issue_note) | Add a note to an issue, optionally replying to a discussion thread | ✏️ | +| [`list_issue_discussions`](core.md#list_issue_discussions) | List discussions for an issue | 📖 | +| [`update_issue_description_patch`](core.md#update_issue_description_patch) | Apply a patch (search/replace or unified diff) to an issue description. Reduces token usage by allowing small changes without sending the full description. Supports dry_run to preview changes and create_note to summarize updates. | ✏️ | +| [`get_file_contents`](core.md#get_file_contents) | Get contents of a file or directory from a GitLab project | 📖 | +| [`get_repository_tree`](core.md#get_repository_tree) | List files and directories in a repository | 📖 | +| [`search_repositories`](core.md#search_repositories) | Search for GitLab projects | 📖 | +| [`get_branch`](core.md#get_branch) | Get branch details (commit, protection status) | 📖 | +| [`list_branches`](core.md#list_branches) | List branches in project with search filter | 📖 | +| [`list_commits`](core.md#list_commits) | List repository commits with filtering options | 📖 | +| [`get_commit`](core.md#get_commit) | Get details of a specific commit | 📖 | +| [`get_commit_diff`](core.md#get_commit_diff) | Get changes/diffs of a specific commit | 📖 | +| [`get_file_blame`](core.md#get_file_blame) | Get git blame for a file at a given ref. Each entry maps a contiguous range of source lines to the commit that last changed them (id, author, authored_date, message). Use range_start/range_end to limit blame to specific lines. | 📖 | +| [`get_project`](core.md#get_project) | Get details of a specific project | 📖 | +| [`list_projects`](core.md#list_projects) | List projects accessible by the current user | 📖 | +| [`list_project_members`](core.md#list_project_members) | List members of a GitLab project | 📖 | +| [`list_labels`](core.md#list_labels) | List labels for a project | 📖 | +| [`whoami`](core.md#whoami) | Get current authenticated user details | 📖 | +| [`health_check`](core.md#health_check) | Verify server status and authentication | 📖 | + ### [Projects & Namespaces](projects.md) Project/namespace listing, member queries, group iterations, and server health. *(10 tools)* +> Opt-in. Enable via `GITLAB_TOOLSETS=projects` (or `GITLAB_TOOLSETS=all`), list individual tools in `GITLAB_TOOLS=`, or activate at runtime with the `discover_tools` MCP tool. + | Tool | What it does | R/W | |---|---|:-:| | [`get_project`](projects.md#get_project) | Get details of a specific project | 📖 | @@ -63,6 +108,8 @@ Project/namespace listing, member queries, group iterations, and server health. Project search/creation/fork plus the Files API for reading and writing repository content without shelling out to git. *(7 tools)* +> Opt-in. Enable via `GITLAB_TOOLSETS=repositories` (or `GITLAB_TOOLSETS=all`), list individual tools in `GITLAB_TOOLS=`, or activate at runtime with the `discover_tools` MCP tool. + | Tool | What it does | R/W | |---|---|:-:| | [`search_repositories`](repositories.md#search_repositories) | Search for GitLab projects | 📖 | @@ -77,6 +124,8 @@ Project search/creation/fork plus the Files API for reading and writing reposito Branch management, commit listing/inspection, file blame, and CI commit-status manipulation. *(15 tools)* +> Opt-in. Enable via `GITLAB_TOOLSETS=branches` (or `GITLAB_TOOLSETS=all`), list individual tools in `GITLAB_TOOLS=`, or activate at runtime with the `discover_tools` MCP tool. + | Tool | What it does | R/W | |---|---|:-:| | [`create_branch`](branches.md#create_branch) | Create a new branch | ✏️ | @@ -99,6 +148,8 @@ Branch management, commit listing/inspection, file blame, and CI commit-status m Create new groups and subgroups. *(1 tools)* +> Opt-in. Enable via `GITLAB_TOOLSETS=groups` (or `GITLAB_TOOLSETS=all`), list individual tools in `GITLAB_TOOLS=`, or activate at runtime with the `discover_tools` MCP tool. + | Tool | What it does | R/W | |---|---|:-:| | [`create_group`](groups.md#create_group) | Create new group or subgroup | ✏️ | @@ -107,6 +158,8 @@ Create new groups and subgroups. *(1 tools)* MR lifecycle — create, update, merge, approve, plus diff/conflict inspection and the full discussion/note/draft API. *(43 tools)* +> Opt-in. Enable via `GITLAB_TOOLSETS=merge_requests` (or `GITLAB_TOOLSETS=all`), list individual tools in `GITLAB_TOOLS=`, or activate at runtime with the `discover_tools` MCP tool. + | Tool | What it does | R/W | |---|---|:-:| | [`merge_merge_request`](merge-requests.md#merge_merge_request) | Merge a merge request | ✏️ | @@ -157,6 +210,8 @@ MR lifecycle — create, update, merge, approve, plus diff/conflict inspection a Issue CRUD, links, discussions and notes, todos, and emoji reactions. *(24 tools)* +> Opt-in. Enable via `GITLAB_TOOLSETS=issues` (or `GITLAB_TOOLSETS=all`), list individual tools in `GITLAB_TOOLS=`, or activate at runtime with the `discover_tools` MCP tool. + | Tool | What it does | R/W | |---|---|:-:| | [`create_issue`](issues.md#create_issue) | Create a new issue | ✏️ | @@ -188,6 +243,8 @@ Issue CRUD, links, discussions and notes, todos, and emoji reactions. *(24 tools Project label CRUD. *(5 tools)* +> Opt-in. Enable via `GITLAB_TOOLSETS=labels` (or `GITLAB_TOOLSETS=all`), list individual tools in `GITLAB_TOOLS=`, or activate at runtime with the `discover_tools` MCP tool. + | Tool | What it does | R/W | |---|---|:-:| | [`list_labels`](labels.md#list_labels) | List labels for a project | 📖 | @@ -227,6 +284,8 @@ Modern unified API for issues, tasks, incidents, and other typed work items — Validate `.gitlab-ci.yml` snippets and project pipeline configs. *(4 tools)* +> Opt-in. Enable via `GITLAB_TOOLSETS=ci` (or `GITLAB_TOOLSETS=all`), list individual tools in `GITLAB_TOOLS=`, or activate at runtime with the `discover_tools` MCP tool. + | Tool | What it does | R/W | |---|---|:-:| | [`validate_ci_lint`](ci.md#validate_ci_lint) | Validate provided GitLab CI/CD YAML content for a project | 📖 | @@ -333,6 +392,8 @@ Tag listing, creation, deletion, and signature inspection. *(5 tools)* User lookup, the authenticated user (`whoami`), event streams, and markdown attachment upload/download. *(7 tools)* +> Opt-in. Enable via `GITLAB_TOOLSETS=users` (or `GITLAB_TOOLSETS=all`), list individual tools in `GITLAB_TOOLS=`, or activate at runtime with the `discover_tools` MCP tool. + | Tool | What it does | R/W | |---|---|:-:| | [`get_users`](users.md#get_users) | Get GitLab user details by usernames | 📖 | @@ -408,7 +469,7 @@ Server diagnostics, tool discovery, and the GraphQL escape hatch. *(2 tools)* | Tool | What it does | R/W | |---|---|:-:| | [`execute_graphql`](meta.md#execute_graphql) | Execute a GitLab GraphQL query | 📖 | -| [`discover_tools`](meta.md#discover_tools) | Discover and activate additional tool categories for this session. Available categories: merge_requests, issues, repositories, branches, projects, labels, ci, groups, pipelines, milestones, wiki, releases, tags, users, workitems, webhooks, search, variables, dependency_proxy. Already-active categories are listed in the response. | 📖 | +| [`discover_tools`](meta.md#discover_tools) | Discover and activate additional tool categories for this session. Available categories: core, merge_requests, issues, repositories, branches, projects, labels, ci, groups, pipelines, milestones, wiki, releases, tags, users, workitems, webhooks, search, variables, dependency_proxy. Already-active categories are listed in the response. | 📖 | --- diff --git a/docs/tools/issues.md b/docs/tools/issues.md index bf9bf554..cd975139 100644 --- a/docs/tools/issues.md +++ b/docs/tools/issues.md @@ -2,6 +2,9 @@ Issue CRUD, links, discussions and notes, todos, and emoji reactions. +!!! note "Feature toggle" + Opt-in. Enable via `GITLAB_TOOLSETS=issues` (or `GITLAB_TOOLSETS=all`), list individual tools in `GITLAB_TOOLS=`, or activate at runtime with the `discover_tools` MCP tool. + ## Tools in this group - [`create_issue`](#create_issue) — ✏️ Writes diff --git a/docs/tools/labels.md b/docs/tools/labels.md index 29cddfdd..b4d47d3b 100644 --- a/docs/tools/labels.md +++ b/docs/tools/labels.md @@ -2,6 +2,9 @@ Project label CRUD. +!!! note "Feature toggle" + Opt-in. Enable via `GITLAB_TOOLSETS=labels` (or `GITLAB_TOOLSETS=all`), list individual tools in `GITLAB_TOOLS=`, or activate at runtime with the `discover_tools` MCP tool. + ## Tools in this group - [`list_labels`](#list_labels) — 📖 Read-only diff --git a/docs/tools/merge-requests.md b/docs/tools/merge-requests.md index f941f669..191df40e 100644 --- a/docs/tools/merge-requests.md +++ b/docs/tools/merge-requests.md @@ -2,6 +2,9 @@ MR lifecycle — create, update, merge, approve, plus diff/conflict inspection and the full discussion/note/draft API. +!!! note "Feature toggle" + Opt-in. Enable via `GITLAB_TOOLSETS=merge_requests` (or `GITLAB_TOOLSETS=all`), list individual tools in `GITLAB_TOOLS=`, or activate at runtime with the `discover_tools` MCP tool. + ## Tools in this group - [`merge_merge_request`](#merge_merge_request) — ✏️ Writes diff --git a/docs/tools/meta.md b/docs/tools/meta.md index c42c6ed8..66bb239e 100644 --- a/docs/tools/meta.md +++ b/docs/tools/meta.md @@ -29,7 +29,7 @@ Execute a GitLab GraphQL query *📖 Read-only* -Discover and activate additional tool categories for this session. Available categories: merge_requests, issues, repositories, branches, projects, labels, ci, groups, pipelines, milestones, wiki, releases, tags, users, workitems, webhooks, search, variables, dependency_proxy. Already-active categories are listed in the response. +Discover and activate additional tool categories for this session. Available categories: core, merge_requests, issues, repositories, branches, projects, labels, ci, groups, pipelines, milestones, wiki, releases, tags, users, workitems, webhooks, search, variables, dependency_proxy. Already-active categories are listed in the response. **Parameters** diff --git a/docs/tools/projects.md b/docs/tools/projects.md index 9df5b62b..0c5afacc 100644 --- a/docs/tools/projects.md +++ b/docs/tools/projects.md @@ -2,6 +2,9 @@ Project/namespace listing, member queries, group iterations, and server health. +!!! note "Feature toggle" + Opt-in. Enable via `GITLAB_TOOLSETS=projects` (or `GITLAB_TOOLSETS=all`), list individual tools in `GITLAB_TOOLS=`, or activate at runtime with the `discover_tools` MCP tool. + ## Tools in this group - [`get_project`](#get_project) — 📖 Read-only diff --git a/docs/tools/repositories.md b/docs/tools/repositories.md index 29f5ed38..4d43432f 100644 --- a/docs/tools/repositories.md +++ b/docs/tools/repositories.md @@ -2,6 +2,9 @@ Project search/creation/fork plus the Files API for reading and writing repository content without shelling out to git. +!!! note "Feature toggle" + Opt-in. Enable via `GITLAB_TOOLSETS=repositories` (or `GITLAB_TOOLSETS=all`), list individual tools in `GITLAB_TOOLS=`, or activate at runtime with the `discover_tools` MCP tool. + ## Tools in this group - [`search_repositories`](#search_repositories) — 📖 Read-only diff --git a/docs/tools/users.md b/docs/tools/users.md index f6e3576e..90ca688f 100644 --- a/docs/tools/users.md +++ b/docs/tools/users.md @@ -2,6 +2,9 @@ User lookup, the authenticated user (`whoami`), event streams, and markdown attachment upload/download. +!!! note "Feature toggle" + Opt-in. Enable via `GITLAB_TOOLSETS=users` (or `GITLAB_TOOLSETS=all`), list individual tools in `GITLAB_TOOLS=`, or activate at runtime with the `discover_tools` MCP tool. + ## Tools in this group - [`get_users`](#get_users) — 📖 Read-only diff --git a/mkdocs.yml b/mkdocs.yml index 5fcbca6e..f3a534c1 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -84,6 +84,7 @@ nav: - Resolve Issue Thread: features/resolve-issue-thread.md - Tools: - Overview: tools/index.md + - Core: tools/core.md - Projects & Namespaces: tools/projects.md - Projects & Files: tools/repositories.md - Branches & Commits: tools/branches.md diff --git a/scripts/generate-tool-docs.ts b/scripts/generate-tool-docs.ts index bee3b547..07c35268 100644 --- a/scripts/generate-tool-docs.ts +++ b/scripts/generate-tool-docs.ts @@ -52,6 +52,11 @@ function computeToggleNote(id: ToolsetId): string | undefined { } const GROUP_META: Record = { + core: { + title: "Core", + blurb: + "Lean default starter set for common MR, issue, repository, branch, project, label, and identity workflows.", + }, merge_requests: { title: "Merge Requests", blurb: @@ -138,6 +143,7 @@ const GROUP_META: Record = { }; const GROUP_ORDER: ToolsetId[] = [ + "core", "projects", "repositories", "branches", @@ -268,7 +274,9 @@ function buildGroupPage(id: ToolsetId, toolNames: string[]): string { for (const name of toolNames) { const tool = allTools.find(t => t.name === name); if (!tool) { - throw new Error(`Tool '${name}' referenced in toolset '${id}' but missing from allTools registry`); + throw new Error( + `Tool '${name}' referenced in toolset '${id}' but missing from allTools registry` + ); } lines.push(toolSection(name, tool.description, tool.inputSchema as JsonSchema)); } @@ -301,6 +309,7 @@ function buildToggleSection(groupedToolsList: Array<[ToolsetId, string[]]>): str "", "- `GITLAB_TOOLSETS=` — comma-separated toolset IDs.", "- `GITLAB_TOOLSETS=all` — enables every group.", + "- `GITLAB_TOOLSETS=merge_requests,issues,repositories,branches,projects,labels,ci,groups,users` — restores the pre-lean default set.", "- `GITLAB_TOOLS=` — enables individual tools regardless of group.", "- `USE_PIPELINE=true` / `USE_MILESTONE=true` / `USE_GITLAB_WIKI=true` —" + " legacy single-group flags (Pipelines, Milestones, Wiki only).", diff --git a/skills/gitlab-mcp/SKILL.md b/skills/gitlab-mcp/SKILL.md index 6022594b..22439688 100644 --- a/skills/gitlab-mcp/SKILL.md +++ b/skills/gitlab-mcp/SKILL.md @@ -5,30 +5,34 @@ description: Use this skill when working with the GitLab MCP server tools for me # gitlab-mcp -GitLab MCP server providing 173 tools: 171 tools across 16 toolsets, plus `execute_graphql` and the always-available `discover_tools` meta-tool. +GitLab MCP server providing 204 tools total: 202 tools across 20 toolsets, plus `execute_graphql` and the always-available `discover_tools` meta-tool. ## Toolsets -| Toolset | Default | Enable with | -| ------------------------- | ------- | ---------------------------------------------------- | -| merge_requests (41 tools) | yes | - | -| issues (23 tools) | yes | - | -| repositories (7 tools) | yes | - | -| branches (6 tools) | yes | - | -| projects (8 tools) | yes | - | -| labels (5 tools) | yes | - | -| ci (2 tools) | yes | - | -| users (5 tools) | yes | - | -| pipelines (19 tools) | no | `USE_PIPELINE=true` or `GITLAB_TOOLSETS=pipelines` | -| milestones (9 tools) | no | `USE_MILESTONE=true` or `GITLAB_TOOLSETS=milestones` | -| wiki (10 tools) | no | `USE_GITLAB_WIKI=true` or `GITLAB_TOOLSETS=wiki` | -| releases (7 tools) | no | `GITLAB_TOOLSETS=releases` | -| tags (5 tools) | no | `GITLAB_TOOLSETS=tags` | -| workitems (18 tools) | no | `GITLAB_TOOLSETS=workitems` | -| webhooks (3 tools) | no | `GITLAB_TOOLSETS=webhooks` | -| search (3 tools) | no | `GITLAB_TOOLSETS=search` | - -Enable all: `GITLAB_TOOLSETS=all`. Use `GITLAB_TOOLS` to enable individual tools outside their toolset. `discover_tools` can activate opt-in categories for the current session. +| Toolset | Default | Enable with | +| -------------------------- | ------- | ---------------------------------------------------- | +| core (35 tools) | yes | default lean starter set | +| merge_requests (43 tools) | no | `GITLAB_TOOLSETS=merge_requests` | +| issues (24 tools) | no | `GITLAB_TOOLSETS=issues` | +| repositories (7 tools) | no | `GITLAB_TOOLSETS=repositories` | +| branches (15 tools) | no | `GITLAB_TOOLSETS=branches` | +| projects (10 tools) | no | `GITLAB_TOOLSETS=projects` | +| labels (5 tools) | no | `GITLAB_TOOLSETS=labels` | +| ci (4 tools) | no | `GITLAB_TOOLSETS=ci` | +| groups (1 tool) | no | `GITLAB_TOOLSETS=groups` | +| users (7 tools) | no | `GITLAB_TOOLSETS=users` | +| pipelines (19 tools) | no | `USE_PIPELINE=true` or `GITLAB_TOOLSETS=pipelines` | +| milestones (9 tools) | no | `USE_MILESTONE=true` or `GITLAB_TOOLSETS=milestones` | +| wiki (10 tools) | no | `USE_GITLAB_WIKI=true` or `GITLAB_TOOLSETS=wiki` | +| releases (7 tools) | no | `GITLAB_TOOLSETS=releases` | +| tags (5 tools) | no | `GITLAB_TOOLSETS=tags` | +| workitems (18 tools) | no | `GITLAB_TOOLSETS=workitems` | +| webhooks (3 tools) | no | `GITLAB_TOOLSETS=webhooks` | +| search (3 tools) | no | `GITLAB_TOOLSETS=search` | +| variables (10 tools) | no | `GITLAB_TOOLSETS=variables` | +| dependency_proxy (4 tools) | no | `GITLAB_TOOLSETS=dependency_proxy` | + +Enable all: `GITLAB_TOOLSETS=all`. Restore the pre-lean default with `GITLAB_TOOLSETS=merge_requests,issues,repositories,branches,projects,labels,ci,groups,users`. Use `GITLAB_TOOLS` to enable individual tools outside their toolset. `discover_tools` can activate opt-in categories for the current session. ## Key Workflows diff --git a/test/test-ci-lint.ts b/test/test-ci-lint.ts index 1e40a502..544e4d99 100644 --- a/test/test-ci-lint.ts +++ b/test/test-ci-lint.ts @@ -220,10 +220,11 @@ describe("GitLab CI lint tools", () => { assert.strictEqual(result.jobs[0].name, "include-job"); }); - test("CI lint tools are visible by default in read-only mode", async () => { + test("CI lint tools are visible when ci toolset is enabled in read-only mode", async () => { const tools = await listToolNames({ GITLAB_API_URL: `${mockGitLabUrl}/api/v4`, GITLAB_PERSONAL_ACCESS_TOKEN: MOCK_TOKEN, + GITLAB_TOOLSETS: "ci", GITLAB_READ_ONLY_MODE: "true", }); diff --git a/test/test-todos.ts b/test/test-todos.ts index 57ec9e55..2e6bd372 100644 --- a/test/test-todos.ts +++ b/test/test-todos.ts @@ -221,10 +221,11 @@ describe("GitLab todos tools", () => { }); }); - test("todo tools are visible in the default issues toolset", async () => { + test("todo tools are visible when the issues toolset is enabled", async () => { const tools = await listToolNames({ GITLAB_API_URL: `${mockGitLabUrl}/api/v4`, GITLAB_PERSONAL_ACCESS_TOKEN: MOCK_TOKEN, + GITLAB_TOOLSETS: "issues", }); assert.ok(tools.includes("list_todos")); @@ -232,10 +233,11 @@ describe("GitLab todos tools", () => { assert.ok(tools.includes("mark_all_todos_done")); }); - test("read-only mode keeps list_todos and hides todo mutations", async () => { + test("read-only mode keeps list_todos and hides todo mutations when issues are enabled", async () => { const tools = await listToolNames({ GITLAB_API_URL: `${mockGitLabUrl}/api/v4`, GITLAB_PERSONAL_ACCESS_TOKEN: MOCK_TOKEN, + GITLAB_TOOLSETS: "issues", GITLAB_READ_ONLY_MODE: "true", }); diff --git a/test/test-toolset-filtering.ts b/test/test-toolset-filtering.ts index 04fb42f9..f8545f93 100644 --- a/test/test-toolset-filtering.ts +++ b/test/test-toolset-filtering.ts @@ -16,11 +16,9 @@ import { TransportMode, HOST, } from "./utils/server-launcher.js"; -import { - MockGitLabServer, - findMockServerPort, -} from "./utils/mock-gitlab-server.js"; +import { MockGitLabServer, findMockServerPort } from "./utils/mock-gitlab-server.js"; import { CustomHeaderClient } from "./clients/custom-header-client.js"; +import { TOOLSET_DEFINITIONS } from "../tools/registry.js"; const MOCK_TOKEN = "glpat-toolset-test-token"; @@ -29,79 +27,64 @@ const MOCK_PORT_BASE = 9200; const MCP_PORT_BASE = 3200; // Known tool counts per toolset (from TOOLSET_DEFINITIONS) -const TOOLSET_TOOL_COUNTS: Record = { - merge_requests: 41, - issues: 24, - repositories: 7, - branches: 15, - projects: 10, - labels: 5, - ci: 4, - pipelines: 19, - milestones: 9, - wiki: 10, - releases: 7, - tags: 5, - users: 7, - search: 3, - workitems: 18, - webhooks: 3, - groups: 1, - variables: 10, - dependency_proxy: 4, -}; +const TOOLSET_TOOL_COUNTS: Record = Object.fromEntries( + TOOLSET_DEFINITIONS.map(def => [def.id, def.tools.size]) +); const LEGACY_PIPELINE_CI_TOOL_COUNT = 2; const LEGACY_PIPELINE_TOOL_COUNT = TOOLSET_TOOL_COUNTS.pipelines + LEGACY_PIPELINE_CI_TOOL_COUNT; -const DEFAULT_TOOLSETS = [ - "merge_requests", - "issues", - "repositories", - "branches", - "projects", - "labels", - "ci", - "users", - "groups", -]; - -const NON_DEFAULT_TOOLSETS = [ - "pipelines", - "milestones", - "wiki", - "releases", - "tags", - "workitems", - "webhooks", - "search", - "variables", - "dependency_proxy", -]; +const DEFAULT_TOOLSETS = TOOLSET_DEFINITIONS.filter(def => def.isDefault).map(def => def.id); + +const NON_DEFAULT_TOOLSETS = TOOLSET_DEFINITIONS.filter(def => !def.isDefault).map(def => def.id); // discover_tools meta-tool is always force-injected (Step 5.5) const DISCOVER_TOOLS_COUNT = 1; -const DEFAULT_TOOL_COUNT = DEFAULT_TOOLSETS.reduce( - (sum, id) => sum + TOOLSET_TOOL_COUNTS[id], - 0 -) + DISCOVER_TOOLS_COUNT; +function uniqueToolCount(toolsetIds: readonly string[]): number { + const names = new Set(); + for (const id of toolsetIds) { + const def = TOOLSET_DEFINITIONS.find(def => def.id === id); + if (!def) continue; + for (const tool of def.tools) names.add(tool); + } + return names.size; +} -const ALL_TOOLSET_TOOL_COUNT = Object.values(TOOLSET_TOOL_COUNTS).reduce( - (sum, c) => sum + c, - 0 -) + DISCOVER_TOOLS_COUNT; +const DEFAULT_TOOL_COUNT = uniqueToolCount(DEFAULT_TOOLSETS) + DISCOVER_TOOLS_COUNT; +const ALL_TOOLSET_TOOL_COUNT = + uniqueToolCount(TOOLSET_DEFINITIONS.map(def => def.id)) + DISCOVER_TOOLS_COUNT; // Representative tools per toolset for spot-checking const TOOLSET_SAMPLE_TOOLS: Record = { + core: ["list_merge_requests", "get_file_contents", "list_issues", "whoami"], merge_requests: ["merge_merge_request", "create_merge_request_thread", "list_draft_notes"], issues: ["create_issue", "list_issues", "create_note", "list_todos"], repositories: ["search_repositories", "get_file_contents", "push_files"], - branches: ["create_branch", "get_branch", "list_branches", "delete_branch", "list_commits", "list_commit_statuses", "create_commit_status"], + branches: [ + "create_branch", + "get_branch", + "list_branches", + "delete_branch", + "list_commits", + "list_commit_statuses", + "create_commit_status", + ], projects: ["get_project", "update_project", "list_namespaces", "list_group_iterations"], labels: ["list_labels", "create_label"], - ci: ["validate_ci_lint", "validate_project_ci_lint", "list_ci_catalog_resources", "get_ci_catalog_resource"], - pipelines: ["list_pipelines", "create_pipeline", "cancel_pipeline_job", "list_deployments", "list_job_artifacts"], + ci: [ + "validate_ci_lint", + "validate_project_ci_lint", + "list_ci_catalog_resources", + "get_ci_catalog_resource", + ], + pipelines: [ + "list_pipelines", + "create_pipeline", + "cancel_pipeline_job", + "list_deployments", + "list_job_artifacts", + ], milestones: ["list_milestones", "create_milestone", "get_milestone_burndown_events"], wiki: ["list_wiki_pages", "create_wiki_page", "list_group_wiki_pages", "create_group_wiki_page"], releases: ["list_releases", "create_release", "download_release_asset"], @@ -110,8 +93,19 @@ const TOOLSET_SAMPLE_TOOLS: Record = { search: ["search_code", "search_project_code", "search_group_code"], webhooks: ["list_webhooks", "list_webhook_events", "get_webhook_event"], groups: ["create_group"], - variables: ["list_project_variables", "create_project_variable", "delete_project_variable", "list_group_variables", "create_group_variable", "delete_group_variable"], - dependency_proxy: ["get_dependency_proxy_settings", "list_dependency_proxy_blobs", "purge_dependency_proxy_cache"], + variables: [ + "list_project_variables", + "create_project_variable", + "delete_project_variable", + "list_group_variables", + "create_group_variable", + "delete_group_variable", + ], + dependency_proxy: [ + "get_dependency_proxy_settings", + "list_dependency_proxy_blobs", + "purge_dependency_proxy_cache", + ], }; // --- Helpers --- @@ -212,6 +206,22 @@ describe("Toolset Filtering", { concurrency: 1 }, () => { assertContainsNone(tools, TOOLSET_SAMPLE_TOOLS.wiki, "non-default wiki"); }); + test("excludes advanced and destructive tools from the lean default", () => { + assertContainsNone( + tools, + [ + "merge_merge_request", + "create_draft_note", + "delete_issue", + "push_files", + "create_label", + "validate_ci_lint", + "create_group", + ], + "advanced defaults" + ); + }); + test("excludes execute_graphql (not in any toolset)", () => { assertContainsNone(tools, ["execute_graphql"], "unassigned"); }); @@ -470,10 +480,12 @@ describe("Toolset Filtering", { concurrency: 1 }, () => { after(() => cleanupServers([server])); test("excludes tools matching the denial regex", () => { - const denied = tools.filter( - (t) => t.startsWith("create_") || t.startsWith("delete_") + const denied = tools.filter(t => t.startsWith("create_") || t.startsWith("delete_")); + assert.strictEqual( + denied.length, + 0, + `Should have no create_/delete_ tools, found: ${denied}` ); - assert.strictEqual(denied.length, 0, `Should have no create_/delete_ tools, found: ${denied}`); }); test("keeps non-matching issue tools", () => { diff --git a/tools/registry.ts b/tools/registry.ts index 075cab1a..ecd97a15 100644 --- a/tools/registry.ts +++ b/tools/registry.ts @@ -1479,6 +1479,7 @@ export const pipelineToolNames = new Set([ // --- Toolset definitions --- export type ToolsetId = + | "core" | "merge_requests" | "issues" | "repositories" @@ -1507,8 +1508,49 @@ export interface ToolsetDefinition { export const TOOLSET_DEFINITIONS: readonly ToolsetDefinition[] = [ { - id: "merge_requests", + id: "core", isDefault: true, + tools: new Set([ + "list_merge_requests", + "get_merge_request", + "get_merge_request_approval_state", + "list_merge_request_changed_files", + "get_merge_request_file_diff", + "list_merge_request_diffs", + "get_merge_request_diffs", + "mr_discussions", + "create_merge_request", + "create_merge_request_thread", + "resolve_merge_request_thread", + "update_merge_request", + "list_issues", + "my_issues", + "get_issue", + "create_issue", + "update_issue", + "create_issue_note", + "list_issue_discussions", + "update_issue_description_patch", + "get_file_contents", + "get_repository_tree", + "search_repositories", + "get_branch", + "list_branches", + "list_commits", + "get_commit", + "get_commit_diff", + "get_file_blame", + "get_project", + "list_projects", + "list_project_members", + "list_labels", + "whoami", + "health_check", + ]), + }, + { + id: "merge_requests", + isDefault: false, tools: new Set([ "merge_merge_request", "approve_merge_request", @@ -1557,7 +1599,7 @@ export const TOOLSET_DEFINITIONS: readonly ToolsetDefinition[] = [ }, { id: "issues", - isDefault: true, + isDefault: false, tools: new Set([ "create_issue", "list_issues", @@ -1587,7 +1629,7 @@ export const TOOLSET_DEFINITIONS: readonly ToolsetDefinition[] = [ }, { id: "repositories", - isDefault: true, + isDefault: false, tools: new Set([ "search_repositories", "create_repository", @@ -1600,7 +1642,7 @@ export const TOOLSET_DEFINITIONS: readonly ToolsetDefinition[] = [ }, { id: "branches", - isDefault: true, + isDefault: false, tools: new Set([ "create_branch", "get_branch", @@ -1621,7 +1663,7 @@ export const TOOLSET_DEFINITIONS: readonly ToolsetDefinition[] = [ }, { id: "projects", - isDefault: true, + isDefault: false, tools: new Set([ "get_project", "list_projects", @@ -1637,7 +1679,7 @@ export const TOOLSET_DEFINITIONS: readonly ToolsetDefinition[] = [ }, { id: "labels", - isDefault: true, + isDefault: false, tools: new Set([ "list_labels", "get_label", @@ -1648,7 +1690,7 @@ export const TOOLSET_DEFINITIONS: readonly ToolsetDefinition[] = [ }, { id: "ci", - isDefault: true, + isDefault: false, tools: new Set([ "validate_ci_lint", "validate_project_ci_lint", @@ -1658,7 +1700,7 @@ export const TOOLSET_DEFINITIONS: readonly ToolsetDefinition[] = [ }, { id: "groups", - isDefault: true, + isDefault: false, tools: new Set(["create_group"]), }, { @@ -1743,7 +1785,7 @@ export const TOOLSET_DEFINITIONS: readonly ToolsetDefinition[] = [ }, { id: "users", - isDefault: true, + isDefault: false, tools: new Set([ "get_users", "get_user", @@ -1820,15 +1862,17 @@ export const TOOLSET_DEFINITIONS: readonly ToolsetDefinition[] = [ }, ] as const; -// Derived lookup: tool name → toolset ID +// Derived lookup: tool name → toolset IDs. Most tools belong to one category. +// The default `core` toolset intentionally overlaps with the full opt-in +// categories so users get a small starter surface without losing the ability +// to enable complete categories later. +export const TOOLSETS_BY_TOOL_NAME = new Map>(); export const TOOLSET_BY_TOOL_NAME = new Map(); for (const def of TOOLSET_DEFINITIONS) { for (const tool of def.tools) { - if (TOOLSET_BY_TOOL_NAME.has(tool)) { - console.warn( - `Tool "${tool}" is defined in multiple toolsets: "${TOOLSET_BY_TOOL_NAME.get(tool)}" and "${def.id}"` - ); - } + const toolsets = TOOLSETS_BY_TOOL_NAME.get(tool) ?? new Set(); + toolsets.add(def.id); + TOOLSETS_BY_TOOL_NAME.set(tool, toolsets); TOOLSET_BY_TOOL_NAME.set(tool, def.id); } } @@ -1905,8 +1949,11 @@ export function isToolInEnabledToolset( toolName: string, enabledToolsets: ReadonlySet ): boolean { - const toolsetId = TOOLSET_BY_TOOL_NAME.get(toolName); + const toolsetIds = TOOLSETS_BY_TOOL_NAME.get(toolName); // Tools not in any toolset (e.g. execute_graphql) are excluded by default - if (toolsetId === undefined) return false; - return enabledToolsets.has(toolsetId); + if (toolsetIds === undefined) return false; + for (const toolsetId of toolsetIds) { + if (enabledToolsets.has(toolsetId)) return true; + } + return false; }