summaryrefslogtreecommitdiff
path: root/servers
diff options
context:
space:
mode:
Diffstat (limited to 'servers')
-rw-r--r--servers/gitlab_python/src/mcp_server_gitlab_python/server.py127
-rw-r--r--servers/gitlab_python/tests/test_server.py58
2 files changed, 185 insertions, 0 deletions
diff --git a/servers/gitlab_python/src/mcp_server_gitlab_python/server.py b/servers/gitlab_python/src/mcp_server_gitlab_python/server.py
index 05e84c5..c176832 100644
--- a/servers/gitlab_python/src/mcp_server_gitlab_python/server.py
+++ b/servers/gitlab_python/src/mcp_server_gitlab_python/server.py
@@ -152,6 +152,33 @@ class GitLabPythonServer:
except Exception as e:
return {"error": str(e)}
+ def update_issue(
+ self,
+ project: str,
+ issue_iid: int,
+ **kwargs: object,
+ ) -> dict[str, Any]:
+ """Update an existing GitLab issue.
+
+ Args:
+ project (str): The project full path or ID.
+ issue_iid (int): The internal ID of the issue.
+ **kwargs: Fields to update (e.g., title, description, labels, etc.).
+
+ Returns:
+ dict[str, Any]: Dictionary with the issue URL or error message.
+ """
+ try:
+ proj = self.gl.projects.get(project)
+ issue = proj.issues.get(issue_iid)
+ for k, v in kwargs.items():
+ setattr(issue, k, v)
+ issue.save()
+ return {"url": issue.web_url}
+ except Exception as e:
+ logger.error(f"Failed to update issue {project}#{issue_iid}: {e}")
+ return {"error": str(e)}
+
def get_mr_diff(
self,
project: str,
@@ -335,6 +362,33 @@ class GitLabPythonServer:
except Exception as e:
return {"error": str(e)}
+ def update_epic(
+ self,
+ group: str,
+ epic_iid: int,
+ **kwargs: object,
+ ) -> dict[str, Any]:
+ """Update an existing GitLab epic.
+
+ Args:
+ group (str): The group full path or ID.
+ epic_iid (int): The internal ID of the epic.
+ **kwargs: Fields to update (e.g., title, description, labels, etc.).
+
+ Returns:
+ dict[str, Any]: Dictionary with the epic URL or error message.
+ """
+ try:
+ grp = self.gl.groups.get(group)
+ epic = grp.epics.get(epic_iid)
+ for k, v in kwargs.items():
+ setattr(epic, k, v)
+ epic.save()
+ return {"url": epic.web_url}
+ except Exception as e:
+ logger.error(f"Failed to update epic {group}#{epic_iid}: {e}")
+ return {"error": str(e)}
+
def create_server(host: str = "127.0.0.1", port: int = 8080) -> FastMCP:
mcp = FastMCP("GitLab Python", host=host, port=port)
@@ -391,6 +445,37 @@ def create_server(host: str = "127.0.0.1", port: int = 8080) -> FastMCP:
return server.create_issue(project, title, description, **data)
@mcp.tool()
+ def update_issue(
+ project: str,
+ issue_iid: int,
+ working_directory: str,
+ title: str | None = None,
+ description: str | None = None,
+ state_event: str | None = None,
+ labels: list[str] | None = None,
+ assignee_ids: list[int] | None = None,
+ milestone_id: int | None = None,
+ **kwargs: object
+ ) -> dict[str, Any]:
+ """Update an existing GitLab issue."""
+ server = GitLabPythonServer(working_directory)
+ data = {}
+ if title is not None:
+ data["title"] = title
+ if description is not None:
+ data["description"] = description
+ if state_event is not None:
+ data["state_event"] = state_event
+ if labels is not None:
+ data["labels"] = labels
+ if assignee_ids is not None:
+ data["assignee_ids"] = assignee_ids
+ if milestone_id is not None:
+ data["milestone_id"] = milestone_id
+ data.update(kwargs)
+ return server.update_issue(project, issue_iid, **data)
+
+ @mcp.tool()
def get_mr_diff(
project: str,
mr_iid: int,
@@ -489,6 +574,48 @@ def create_server(host: str = "127.0.0.1", port: int = 8080) -> FastMCP:
data.update(kwargs)
return server.create_epic(group, title, description, **data)
+ @mcp.tool()
+ def update_epic(
+ group: str,
+ epic_iid: int,
+ working_directory: str,
+ title: str | None = None,
+ description: str | None = None,
+ state_event: str | None = None,
+ labels: list[str] | None = None,
+ parent_id: int | None = None,
+ color: str | None = None,
+ confidential: bool | None = None,
+ start_date_fixed: str | None = None,
+ due_date_fixed: str | None = None,
+ **kwargs: object
+ ) -> dict[str, Any]:
+ """Update an existing GitLab epic."""
+ server = GitLabPythonServer(working_directory)
+ data = {}
+ if title is not None:
+ data["title"] = title
+ if description is not None:
+ data["description"] = description
+ if state_event is not None:
+ data["state_event"] = state_event
+ if labels is not None:
+ data["labels"] = labels
+ if parent_id is not None:
+ data["parent_id"] = parent_id
+ if color is not None:
+ data["color"] = color
+ if confidential is not None:
+ data["confidential"] = confidential
+ if start_date_fixed is not None:
+ data["start_date_fixed"] = start_date_fixed
+ data["start_date_is_fixed"] = True
+ if due_date_fixed is not None:
+ data["due_date_fixed"] = due_date_fixed
+ data["due_date_is_fixed"] = True
+ data.update(kwargs)
+ return server.update_epic(group, epic_iid, **data)
+
return mcp
async def main(transport_type: str, host: str, port: int) -> None:
diff --git a/servers/gitlab_python/tests/test_server.py b/servers/gitlab_python/tests/test_server.py
index 7c0e69f..244cdd5 100644
--- a/servers/gitlab_python/tests/test_server.py
+++ b/servers/gitlab_python/tests/test_server.py
@@ -277,4 +277,62 @@ def test_create_epic_error(mock_gitlab, mock_settings):
mock_gitlab.return_value.groups.get.side_effect = Exception("Group not found")
result = server.create_epic("bad-group", "Epic Title", "Epic description")
assert "error" in result
+ assert "Group not found" in result["error"]
+
+@patch("mcp_server_gitlab_python.server.get_gitlab_settings", return_value=("https://gitlab.com", "dummy-token"))
+@patch("gitlab.Gitlab")
+def test_update_issue_success(mock_gitlab, mock_settings):
+ """Test successful update of a GitLab issue."""
+ server = GitLabPythonServer(working_directory="/tmp")
+ proj = MagicMock()
+ issue = MagicMock()
+ issue.web_url = "https://gitlab.com/project/-/issues/42"
+ proj.issues.get.return_value = issue
+ mock_gitlab.return_value.projects.get.return_value = proj
+ result = server.update_issue("project/path", 42, title="Updated Title", description="Updated desc", labels=["bug"])
+ assert "url" in result
+ assert result["url"] == "https://gitlab.com/project/-/issues/42"
+ proj.issues.get.assert_called_once_with(42)
+ assert issue.title == "Updated Title"
+ assert issue.description == "Updated desc"
+ assert issue.labels == ["bug"]
+ issue.save.assert_called_once()
+
+@patch("mcp_server_gitlab_python.server.get_gitlab_settings", return_value=("https://gitlab.com", "dummy-token"))
+@patch("gitlab.Gitlab")
+def test_update_issue_error(mock_gitlab, mock_settings):
+ """Test error handling for update_issue when project or issue is not found."""
+ server = GitLabPythonServer(working_directory="/tmp")
+ mock_gitlab.return_value.projects.get.side_effect = Exception("Project not found")
+ result = server.update_issue("bad-project", 42, title="Updated Title")
+ assert "error" in result
+ assert "Project not found" in result["error"]
+
+@patch("mcp_server_gitlab_python.server.get_gitlab_settings", return_value=("https://gitlab.com", "dummy-token"))
+@patch("gitlab.Gitlab")
+def test_update_epic_success(mock_gitlab, mock_settings):
+ """Test successful update of a GitLab epic."""
+ server = GitLabPythonServer(working_directory="/tmp")
+ group = MagicMock()
+ epic = MagicMock()
+ epic.web_url = "https://gitlab.com/groups/test-group/-/epics/101"
+ group.epics.get.return_value = epic
+ mock_gitlab.return_value.groups.get.return_value = group
+ result = server.update_epic("test-group", 101, title="Updated Epic", description="Updated desc", labels=["feature"])
+ assert "url" in result
+ assert result["url"] == "https://gitlab.com/groups/test-group/-/epics/101"
+ group.epics.get.assert_called_once_with(101)
+ assert epic.title == "Updated Epic"
+ assert epic.description == "Updated desc"
+ assert epic.labels == ["feature"]
+ epic.save.assert_called_once()
+
+@patch("mcp_server_gitlab_python.server.get_gitlab_settings", return_value=("https://gitlab.com", "dummy-token"))
+@patch("gitlab.Gitlab")
+def test_update_epic_error(mock_gitlab, mock_settings):
+ """Test error handling for update_epic when group or epic is not found."""
+ server = GitLabPythonServer(working_directory="/tmp")
+ mock_gitlab.return_value.groups.get.side_effect = Exception("Group not found")
+ result = server.update_epic("bad-group", 101, title="Updated Epic")
+ assert "error" in result
assert "Group not found" in result["error"] \ No newline at end of file