diff options
| author | Dawid Rycerz <dawid@rycerz.xyz> | 2025-05-28 15:27:57 +0200 |
|---|---|---|
| committer | Dawid Rycerz <dawid@rycerz.xyz> | 2025-05-28 15:27:57 +0200 |
| commit | 0b819edceb307ce2f8ba6d58b37a86329b7d6ec0 (patch) | |
| tree | f8bc71742a93cd3cb6baa55ce81d14ad42c42720 /servers/gitlab_glab/src/mcp_server_gitlab_glab | |
| parent | a56a1ea5fa18c28d2fc3d42bfa780c026ce9b52d (diff) | |
feat(gitlab_glab): add merge request diff functionality with large diff mitigation
- Add get_mr_diff method to GitLabServer class with support for all glab mr diff options
- Implement smart large diff handling that saves diffs > 100KB to temporary files
- Add comprehensive test coverage (7 new tests) achieving 95% coverage
- Support for MR ID/branch, color options, raw format, and repository selection
- Include proper error handling and logging
- Update README with usage examples and documentation
- Follow project coding standards with type hints and docstrings
Resolves large diff handling for LLM consumption while preserving full diff access.
Diffstat (limited to 'servers/gitlab_glab/src/mcp_server_gitlab_glab')
| -rw-r--r-- | servers/gitlab_glab/src/mcp_server_gitlab_glab/server.py | 126 |
1 files changed, 126 insertions, 0 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 d3fd074..881228f 100644 --- a/servers/gitlab_glab/src/mcp_server_gitlab_glab/server.py +++ b/servers/gitlab_glab/src/mcp_server_gitlab_glab/server.py @@ -11,6 +11,7 @@ import os import re import subprocess import sys +import tempfile from typing import Any from mcp.server.fastmcp import FastMCP @@ -309,6 +310,98 @@ class GitLabServer: logger.error(f"Failed to extract issue URL from output: {result}") return {"error": "Failed to extract issue URL from command output"} + def get_mr_diff( + self, + working_directory: str, + mr_id: str | None = None, + color: str = "never", + raw: bool = False, + repo: str | None = None, + max_size_kb: int = 100, + ) -> dict[str, Any]: + """Get the diff for a merge request. + + Args: + working_directory: The directory to execute the command in. + mr_id: The merge request ID or branch name. If None, uses current branch. + color: Use color in diff output: always, never, auto (default: never). + raw: Use raw diff format that can be piped to commands. + repo: Select another repository (OWNER/REPO or GROUP/NAMESPACE/REPO format). + max_size_kb: Maximum size in KB before saving to temporary file + (default: 100). + + Returns: + A dictionary containing the diff content or a path to temporary file + if too large. + """ + # Build command arguments + args = ["mr", "diff"] + + # Add MR ID or branch if specified + if mr_id: + args.append(mr_id) + + # Add color option + if color in ["always", "never", "auto"]: + args.extend(["--color", color]) + + # Add raw option + if raw: + args.append("--raw") + + # Add repo option + if repo: + args.extend(["-R", repo]) + + # Execute the command + success, result = self.execute_glab_command(args, working_directory) + + if not success: + return result + + # Check if the diff is too large + diff_content = result + diff_size_kb = len(diff_content.encode('utf-8')) / 1024 + + if diff_size_kb > max_size_kb: + try: + # Create a temporary file to store the large diff + with tempfile.NamedTemporaryFile( + mode='w', + suffix='.diff', + prefix='mr_diff_', + delete=False, + encoding='utf-8' + ) as temp_file: + temp_file.write(diff_content) + temp_path = temp_file.name + + return { + "diff_too_large": True, + "size_kb": round(diff_size_kb, 2), + "max_size_kb": max_size_kb, + "temp_file_path": temp_path, + "message": ( + f"Diff is too large ({diff_size_kb:.2f} KB > " + f"{max_size_kb} KB). Content saved to temporary file: " + f"{temp_path}" + ) + } + except Exception as e: + logger.error(f"Failed to create temporary file: {str(e)}") + return { + "error": ( + f"Diff is too large ({diff_size_kb:.2f} KB) and failed to " + f"create temporary file: {str(e)}" + ) + } + + return { + "diff": diff_content, + "size_kb": round(diff_size_kb, 2), + "temp_file_path": None + } + def create_server(host: str = "127.0.0.1", port: int = 8080) -> FastMCP: """Create and configure the FastMCP server. @@ -455,6 +548,39 @@ def create_server(host: str = "127.0.0.1", port: int = 8080) -> FastMCP: project=project, ) + @mcp.tool() + def get_mr_diff( + working_directory: str, + mr_id: str | None = None, + color: str = "never", + raw: bool = False, + repo: str | None = None, + max_size_kb: int = 100, + ) -> dict[str, Any]: + """Get the diff for a merge request. + + Args: + working_directory: The directory to execute the command in. + mr_id: The merge request ID or branch name. If None, uses current branch. + color: Use color in diff output: always, never, auto (default: never). + raw: Use raw diff format that can be piped to commands. + repo: Select another repository (OWNER/REPO or GROUP/NAMESPACE/REPO format). + max_size_kb: Maximum size in KB before saving to temporary file + (default: 100). + + Returns: + A dictionary containing the diff content or a path to temporary file + if too large. + """ + return gitlab.get_mr_diff( + working_directory=working_directory, + mr_id=mr_id, + color=color, + raw=raw, + repo=repo, + max_size_kb=max_size_kb, + ) + return mcp |
