summaryrefslogtreecommitdiff
path: root/servers/gitlab_glab/src
diff options
context:
space:
mode:
authorDawid Rycerz <dawid@rycerz.xyz>2025-04-05 21:30:25 +0200
committerDawid Rycerz <dawid@rycerz.xyz>2025-04-05 21:30:25 +0200
commitfdfb3abd3c595e4c5c42b4d854b152262c3b4614 (patch)
tree209a4f23eb83cc3c6197f7097dd068c2ad024783 /servers/gitlab_glab/src
parentfa91e3fcbdd3b1d70f01de256e1c48fe7726ebd4 (diff)
feat: add listing gitlab issues to glab mcp server
Diffstat (limited to 'servers/gitlab_glab/src')
-rw-r--r--servers/gitlab_glab/src/mcp_server_gitlab_glab/server.py170
1 files changed, 169 insertions, 1 deletions
diff --git a/servers/gitlab_glab/src/mcp_server_gitlab_glab/server.py b/servers/gitlab_glab/src/mcp_server_gitlab_glab/server.py
index 958e0a7..d3fd074 100644
--- a/servers/gitlab_glab/src/mcp_server_gitlab_glab/server.py
+++ b/servers/gitlab_glab/src/mcp_server_gitlab_glab/server.py
@@ -120,7 +120,11 @@ class GitLabServer:
or an error message.
"""
success, result = self.execute_glab_command(
- ["api", f"/projects?search_namespaces=true&search={project_name}"], working_directory
+ [
+ "api",
+ f"/projects?search_namespaces=true&search={project_name}",
+ ],
+ working_directory
)
if not success:
@@ -144,6 +148,109 @@ class GitLabServer:
else:
return {"error": f"Project '{project_name}' not found"}
+ def search_issues(
+ self,
+ working_directory: str,
+ author: str | None = None,
+ assignee: str | None = None,
+ closed: bool = False,
+ confidential: bool = False,
+ group: str | None = None,
+ issue_type: str | None = None,
+ iteration: int | None = None,
+ label: list[str] | None = None,
+ milestone: str | None = None,
+ not_assignee: str | None = None,
+ not_author: str | None = None,
+ not_label: list[str] | None = None,
+ page: int = 1,
+ per_page: int = 30,
+ project: str | None = None,
+ ) -> dict[str, Any]:
+ """Search for GitLab issues with various filters.
+
+ Args:
+ working_directory: The directory to execute the command in.
+ author: Filter issue by author username.
+ assignee: Filter issue by assignee username.
+ closed: Get only closed issues.
+ confidential: Filter by confidential issues.
+ group: Select a group or subgroup.
+ issue_type: Filter issue by its type (issue, incident, test_case).
+ iteration: Filter issue by iteration ID.
+ label: Filter issue by label names.
+ milestone: Filter issue by milestone ID or title.
+ not_assignee: Filter issue by not being assigned to username.
+ not_author: Filter issue by not being by author username.
+ not_label: Filter issue by lack of label names.
+ page: Page number.
+ per_page: Number of items to list per page.
+ project: Select another repository.
+
+ Returns:
+ A dictionary containing a list of issues with their IDs, titles, and links.
+ """
+ # Build command arguments
+ args = ["issue", "list", "-O", "json"]
+
+ # Add filter arguments
+ if author:
+ args.extend(["--author", author])
+ if assignee:
+ args.extend(["-a", assignee])
+ if closed:
+ args.append("-c")
+ if confidential:
+ args.append("-C")
+ if group:
+ args.extend(["-g", group])
+ if issue_type:
+ args.extend(["-t", issue_type])
+ if iteration:
+ args.extend(["-i", str(iteration)])
+ if label:
+ for lbl in label:
+ args.extend(["-l", lbl])
+ if milestone:
+ args.extend(["-m", milestone])
+ if not_assignee:
+ args.extend(["--not-assignee", not_assignee])
+ if not_author:
+ args.extend(["--not-author", not_author])
+ if not_label:
+ for lbl in not_label:
+ args.extend(["--not-label", lbl])
+ if page != 1:
+ args.extend(["-p", str(page)])
+ if per_page != 30:
+ args.extend(["-P", str(per_page)])
+ if project:
+ args.extend(["-R", project])
+
+ # Execute the command
+ success, result = self.execute_glab_command(args, working_directory)
+
+ if not success:
+ return result
+
+ # If the result is already a list (JSON parsed), process it
+ if isinstance(result, list):
+ issues = []
+ for issue in result:
+ issues.append({
+ "id": issue.get("id"),
+ "iid": issue.get("iid"),
+ "title": issue.get("title"),
+ "web_url": issue.get("web_url"),
+ "state": issue.get("state"),
+ "created_at": issue.get("created_at"),
+ "updated_at": issue.get("updated_at"),
+ })
+ return {"issues": issues}
+ else:
+ # This shouldn't happen with -O json, but handle it just in case
+ return {"error": "Failed to parse issues list"}
+
def create_issue(
self,
title: str,
@@ -251,6 +358,67 @@ def create_server(host: str = "127.0.0.1", port: int = 8080) -> FastMCP:
# Add create_issue tool
@mcp.tool()
+ def search_issues(
+ working_directory: str,
+ author: str | None = None,
+ assignee: str | None = None,
+ closed: bool = False,
+ confidential: bool = False,
+ group: str | None = None,
+ issue_type: str | None = None,
+ iteration: int | None = None,
+ label: list[str] | None = None,
+ milestone: str | None = None,
+ not_assignee: str | None = None,
+ not_author: str | None = None,
+ not_label: list[str] | None = None,
+ page: int = 1,
+ per_page: int = 30,
+ project: str | None = None,
+ ) -> dict[str, Any]:
+ """Search for GitLab issues with various filters.
+
+ Args:
+ working_directory: The directory to execute the command in.
+ author: Filter issue by author username.
+ assignee: Filter issue by assignee username.
+ closed: Get only closed issues.
+ confidential: Filter by confidential issues.
+ group: Select a group or subgroup.
+ issue_type: Filter issue by its type (issue, incident, test_case).
+ iteration: Filter issue by iteration ID.
+ label: Filter issue by label names.
+ milestone: Filter issue by milestone ID or title.
+ not_assignee: Filter issue by not being assigned to username.
+ not_author: Filter issue by not being by author username.
+ not_label: Filter issue by lack of label names.
+ page: Page number.
+ per_page: Number of items to list per page.
+ project: Select another repository.
+
+ Returns:
+ A dictionary containing a list of issues with their IDs, titles, and links.
+ """
+ return gitlab.search_issues(
+ working_directory=working_directory,
+ author=author,
+ assignee=assignee,
+ closed=closed,
+ confidential=confidential,
+ group=group,
+ issue_type=issue_type,
+ iteration=iteration,
+ label=label,
+ milestone=milestone,
+ not_assignee=not_assignee,
+ not_author=not_author,
+ not_label=not_label,
+ page=page,
+ per_page=per_page,
+ project=project,
+ )
+
+ @mcp.tool()
def create_issue(
title: str,
description: str,