fetch_merge_request
Retrieve GitLab merge request details and content for code review analysis, including changes, diffs, and metadata from specified projects.
Instructions
Fetch a GitLab merge request and its contents.
Args:
project_id: The GitLab project ID or URL-encoded path
merge_request_iid: The merge request IID (project-specific ID)
Returns:
Dict containing the merge request information
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| project_id | Yes | ||
| merge_request_iid | Yes |
Input Schema (JSON Schema)
{
"properties": {
"merge_request_iid": {
"title": "Merge Request Iid",
"type": "string"
},
"project_id": {
"title": "Project Id",
"type": "string"
}
},
"required": [
"project_id",
"merge_request_iid"
],
"type": "object"
}
Implementation Reference
- server.py:75-172 (handler)The handler function decorated with @mcp.tool() that implements the fetch_merge_request tool. It retrieves the merge request data from GitLab, slims and filters changes based on config exclude_patterns, simplifies commits, and processes discussions and notes.@mcp.tool() def fetch_merge_request(ctx: Context, project_id: str, merge_request_iid: str) -> Dict[str, Any]: """ Fetch a GitLab merge request and its contents. Args: project_id: The GitLab project ID or URL-encoded path merge_request_iid: The merge request IID (project-specific ID) Returns: Dict containing the merge request information """ gl = ctx.request_context.lifespan_context project = gl.projects.get(project_id) mr = project.mergerequests.get(merge_request_iid) # 精简 merge_request 信息 mr_data = mr.asdict() slim_mr = { "id": mr_data.get("id"), "iid": mr_data.get("iid"), "project_id": mr_data.get("project_id"), "title": mr_data.get("title"), "description": mr_data.get("description"), "state": mr_data.get("state"), "author": mr_data.get("author", {}).get("name"), "source_branch": mr_data.get("source_branch"), "target_branch": mr_data.get("target_branch"), } # 获取并过滤 changes original_changes_data = mr.changes() all_changes = original_changes_data.get("changes", []) exclude_patterns = config.get("exclude_patterns", []) filtered_changes_list = [] for change in all_changes: file_path = change.get("new_path") if not is_path_excluded(file_path, exclude_patterns): slim_change = { "new_path": change.get("new_path"), "old_path": change.get("old_path"), "new_file": change.get("new_file"), "renamed_file": change.get("renamed_file"), "deleted_file": change.get("deleted_file"), "diff": change.get("diff") } filtered_changes_list.append(slim_change) # 创建一个只包含必要字段的精简版 changes 对象 slim_changes_obj = { "diff_refs": original_changes_data.get("diff_refs"), "changes": filtered_changes_list } # 精简 commits commits = [ { "id": c.id, "short_id": c.short_id, "title": c.title, "author_name": c.author_name, } for c in mr.commits(all=True) ] def slim_note(note): if not isinstance(note, dict): note = note.asdict() author = note.get("author", {}) return { "id": note.get("id"), "type": note.get("type"), "body": note.get("body"), "system": note.get("system"), "author": author.get("name"), "position": note.get("position", {}), } # 精简 discussions 和其下的 notes all_discussions = mr.discussions.list(all=True) discussions = [] for d in all_discussions: # d.attributes['notes'] 包含了该 discussion 下的所有 note 信息 slim_notes_list = [slim_note(n) for n in d.attributes.get('notes', [])] discussions.append({ "id": d.id, "individual_note": d.individual_note, "notes": slim_notes_list }) return { "merge_request": slim_mr, "changes": slim_changes_obj, "commits": commits, "discussions": discussions, }
- server.py:65-73 (helper)Helper function used within fetch_merge_request to filter out excluded file paths from changes based on config patterns.def is_path_excluded(file_path: str, patterns: List[str]) -> bool: """Check if a file path matches any of the exclusion patterns.""" for pattern in patterns: if pattern.endswith('/'): if file_path.startswith(pattern) or f"/{pattern}" in file_path: return True elif fnmatch.fnmatch(file_path, pattern): return True return False