Skip to main content
Glama

get_commit_discussions

Retrieve discussions and comments on commits within a GitLab merge request to review feedback and track changes.

Instructions

Get discussions and comments on commits within a specific merge request

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
merge_request_iidYesInternal ID of the merge request

Implementation Reference

  • The main handler function that implements the tool logic: fetches merge request commits and discussions, matches discussions to commits based on head_sha, and formats a detailed markdown report with summaries and per-commit details.
    async def get_commit_discussions(gitlab_url, project_id, access_token, args):
        """Get discussions/comments on commits within a merge request"""
        logging.info(f"get_commit_discussions called with args: {args}")
        mr_iid = args["merge_request_iid"]
    
        try:
            commits_result = await get_merge_request_commits(gitlab_url, project_id, access_token, mr_iid)
            commits_status, commits_data, commits_error = commits_result
    
            if commits_status != 200:
                logging.error(f"Error fetching merge request commits: " f"{commits_status} - {commits_error}")
                raise Exception(f"Error fetching merge request commits: {commits_error}")
    
            if not commits_data:
                return [TextContent(type="text", text="No commits found in this merge request.")]
    
            logging.info(f"Getting ALL MR discussions for MR #{mr_iid}...")
            discussions_result = await get_merge_request_discussions_paginated(gitlab_url, project_id, access_token, mr_iid)
            discussions_status, discussions_data, discussions_error = discussions_result
    
            if discussions_status != 200:
                logging.error(f"Error fetching MR discussions: " f"{discussions_status} - {discussions_error}")
                discussions_data = []
    
            commit_map = {commit["id"]: commit for commit in commits_data}
    
            commits_with_discussions = {}
            total_discussions = 0
    
            for discussion in discussions_data:
                notes = discussion.get("notes", [])
                for note in notes:
                    position = note.get("position")
                    if position and position.get("head_sha"):
                        commit_sha = position["head_sha"]
                        if commit_sha in commit_map:
                            if commit_sha not in commits_with_discussions:
                                commits_with_discussions[commit_sha] = {"commit": commit_map[commit_sha], "discussions": []}
                            commits_with_discussions[commit_sha]["discussions"].append(
                                {"discussion_id": discussion.get("id"), "note": note, "position": position}
                            )
                            total_discussions += 1
    
            if not commits_with_discussions:
                summary_text = (
                    f"## Commit Discussions for MR #{mr_iid}\n\n"
                    f"**Summary:**\n"
                    f"- Total commits: {len(commits_data)}\n"
                    f"- Commits with discussions: 0\n"
                    f"- Total discussions: 0\n\n"
                    f"No line-level discussions found on any commits in this "
                    f"merge request. Found {len(discussions_data)} total MR discussions."
                )
                return [TextContent(type="text", text=summary_text)]
    
            response_text = (
                f"## Commit Discussions for MR #{mr_iid}\n\n"
                f"**Summary:**\n"
                f"- Total commits: {len(commits_data)}\n"
                f"- Commits with discussions: {len(commits_with_discussions)}\n"
                f"- Total line-level discussions: {total_discussions}\n"
                f"- Total MR discussions: {len(discussions_data)}\n\n"
            )
    
            for _commit_sha, item in commits_with_discussions.items():
                commit = item["commit"]
                discussions = item["discussions"]
    
                response_text += f"### 📝 Commit: {commit['short_id']}\n"
                response_text += f"**Title:** {commit['title']}\n"
                response_text += f"**Author:** {commit['author_name']}\n"
                response_text += f"**Date:** {format_date(commit['committed_date'])}\n"
                response_text += f"**SHA:** `{commit['id']}`\n\n"
    
                for discussion_item in discussions:
                    discussion_id = discussion_item["discussion_id"]
                    note = discussion_item["note"]
                    position = discussion_item["position"]
    
                    author_name = note["author"]["name"]
                    response_text += f"**💬 Comment by {author_name}:**\n"
                    response_text += f"{note['body']}\n"
    
                    if position.get("new_path"):
                        line_info = position.get("new_line", "N/A")
                        response_text += f"*On file: {position['new_path']} " f"(line {line_info})*\n"
    
                    created_at = format_date(note["created_at"])
                    response_text += f"*Posted: {created_at}*\n"
                    response_text += f"*Discussion ID: {discussion_id}*\n\n"
    
                response_text += "---\n\n"
    
            return [TextContent(type="text", text=response_text)]
    
        except Exception as e:
            logging.error(f"Error in get_commit_discussions: {str(e)}")
            return [TextContent(type="text", text=f"Error retrieving commit discussions: {str(e)}")]
  • The tool schema definition including input schema requiring 'merge_request_iid' as input.
    Tool(
        name="get_commit_discussions",
        description=("Get discussions and comments on commits within a " "specific merge request"),
        inputSchema={
            "type": "object",
            "properties": {
                "merge_request_iid": {
                    "type": "integer",
                    "minimum": 1,
                    "description": ("Internal ID of the merge request"),
                }
            },
            "required": ["merge_request_iid"],
            "additionalProperties": False,
        },
    ),
  • main.py:344-347 (registration)
    The dispatch/registration in the call_tool handler that routes calls to the get_commit_discussions function.
    elif name == "get_commit_discussions":
        return await get_commit_discussions(
            self.config["gitlab_url"], self.config["project_id"], self.config["access_token"], arguments
        )
  • main.py:41-277 (registration)
    The list_tools decorator registration where the tool is listed and described for the MCP server.
    @self.server.list_tools()
    async def list_tools() -> List[Tool]:
        logging.info("list_tools called")
        tools = [
            Tool(
                name="list_merge_requests",
                description="List merge requests for the GitLab project",
                inputSchema={
                    "type": "object",
                    "properties": {
                        "state": {
                            "type": "string",
                            "enum": ["opened", "closed", "merged", "all"],
                            "default": "opened",
                            "description": "Filter by merge request state",
                        },
                        "target_branch": {"type": "string", "description": ("Filter by target branch (optional)")},
                        "limit": {
                            "type": "integer",
                            "default": 10,
                            "minimum": 1,
                            "maximum": 100,
                            "description": "Maximum number of results",
                        },
                    },
                    "additionalProperties": False,
                },
            ),
            Tool(
                name="get_merge_request_reviews",
                description=("Get reviews and discussions for a specific " "merge request"),
                inputSchema={
                    "type": "object",
                    "properties": {
                        "merge_request_iid": {
                            "type": "integer",
                            "minimum": 1,
                            "description": ("Internal ID of the merge request"),
                        }
                    },
                    "required": ["merge_request_iid"],
                    "additionalProperties": False,
                },
            ),
            Tool(
                name="get_merge_request_details",
                description=("Get detailed information about a specific " "merge request"),
                inputSchema={
                    "type": "object",
                    "properties": {
                        "merge_request_iid": {
                            "type": "integer",
                            "minimum": 1,
                            "description": ("Internal ID of the merge request"),
                        }
                    },
                    "required": ["merge_request_iid"],
                    "additionalProperties": False,
                },
            ),
            Tool(
                name="get_merge_request_pipeline",
                description=(
                    "Get the last pipeline data for a specific merge "
                    "request, including all jobs and their statuses. "
                    "Returns job IDs that can be used with get_job_log "
                    "to fetch detailed output."
                ),
                inputSchema={
                    "type": "object",
                    "properties": {
                        "merge_request_iid": {
                            "type": "integer",
                            "minimum": 1,
                            "description": ("Internal ID of the merge request"),
                        }
                    },
                    "required": ["merge_request_iid"],
                    "additionalProperties": False,
                },
            ),
            Tool(
                name="get_merge_request_test_report",
                description=(
                    "Get structured test report for a merge request "
                    "with specific test failures, error messages, and "
                    "stack traces. Shows the same test data visible on "
                    "the GitLab MR page. Best for debugging test failures."
                ),
                inputSchema={
                    "type": "object",
                    "properties": {
                        "merge_request_iid": {
                            "type": "integer",
                            "minimum": 1,
                            "description": ("Internal ID of the merge request"),
                        }
                    },
                    "required": ["merge_request_iid"],
                    "additionalProperties": False,
                },
            ),
            Tool(
                name="get_pipeline_test_summary",
                description=(
                    "Get test summary for a merge request - a "
                    "lightweight overview showing pass/fail counts "
                    "per test suite. Faster than full test report. "
                    "Great for quick status checks."
                ),
                inputSchema={
                    "type": "object",
                    "properties": {
                        "merge_request_iid": {
                            "type": "integer",
                            "minimum": 1,
                            "description": ("Internal ID of the merge request"),
                        }
                    },
                    "required": ["merge_request_iid"],
                    "additionalProperties": False,
                },
            ),
            Tool(
                name="get_job_log",
                description=(
                    "Get the trace/log output for a specific pipeline "
                    "job. Perfect for debugging failed tests and "
                    "understanding CI/CD failures."
                ),
                inputSchema={
                    "type": "object",
                    "properties": {
                        "job_id": {
                            "type": "integer",
                            "minimum": 1,
                            "description": ("ID of the pipeline job (obtained from " "get_merge_request_pipeline)"),
                        }
                    },
                    "required": ["job_id"],
                    "additionalProperties": False,
                },
            ),
            Tool(
                name="get_branch_merge_requests",
                description=("Get all merge requests for a specific branch"),
                inputSchema={
                    "type": "object",
                    "properties": {"branch_name": {"type": "string", "description": "Name of the branch"}},
                    "required": ["branch_name"],
                    "additionalProperties": False,
                },
            ),
            Tool(
                name="reply_to_review_comment",
                description=("Reply to a specific discussion thread in a " "merge request review"),
                inputSchema={
                    "type": "object",
                    "properties": {
                        "merge_request_iid": {
                            "type": "integer",
                            "minimum": 1,
                            "description": ("Internal ID of the merge request"),
                        },
                        "discussion_id": {
                            "type": "string",
                            "description": ("ID of the discussion thread to reply to"),
                        },
                        "body": {"type": "string", "description": "Content of the reply comment"},
                    },
                    "required": ["merge_request_iid", "discussion_id", "body"],
                    "additionalProperties": False,
                },
            ),
            Tool(
                name="create_review_comment",
                description=("Create a new discussion thread in a " "merge request review"),
                inputSchema={
                    "type": "object",
                    "properties": {
                        "merge_request_iid": {
                            "type": "integer",
                            "minimum": 1,
                            "description": ("Internal ID of the merge request"),
                        },
                        "body": {"type": "string", "description": ("Content of the new discussion comment")},
                    },
                    "required": ["merge_request_iid", "body"],
                    "additionalProperties": False,
                },
            ),
            Tool(
                name="resolve_review_discussion",
                description=("Resolve or unresolve a discussion thread in a " "merge request review"),
                inputSchema={
                    "type": "object",
                    "properties": {
                        "merge_request_iid": {
                            "type": "integer",
                            "minimum": 1,
                            "description": ("Internal ID of the merge request"),
                        },
                        "discussion_id": {
                            "type": "string",
                            "description": ("ID of the discussion thread to " "resolve/unresolve"),
                        },
                        "resolved": {
                            "type": "boolean",
                            "default": True,
                            "description": ("Whether to resolve (true) or unresolve " "(false) the discussion"),
                        },
                    },
                    "required": ["merge_request_iid", "discussion_id"],
                    "additionalProperties": False,
                },
            ),
            Tool(
                name="get_commit_discussions",
                description=("Get discussions and comments on commits within a " "specific merge request"),
                inputSchema={
                    "type": "object",
                    "properties": {
                        "merge_request_iid": {
                            "type": "integer",
                            "minimum": 1,
                            "description": ("Internal ID of the merge request"),
                        }
                    },
                    "required": ["merge_request_iid"],
                    "additionalProperties": False,
                },
            ),
        ]
        tool_names = [t.name for t in tools]
        logging.info(f"Returning {len(tools)} tools: {tool_names}")
        return tools
  • Helper function used by the handler to fetch all commits in the merge request with pagination.
    async def get_merge_request_commits(gitlab_url, project_id, access_token, mr_iid):
        """Get all commits in a merge request (handles pagination)"""
        base_url = f"{gitlab_url}/api/v4/projects/{project_id}/" f"merge_requests/{mr_iid}/commits"
        headers = _headers(access_token)
        all_commits = []
        page = 1
        per_page = 100  # Maximum allowed per page
    
        async with aiohttp.ClientSession() as session:
            while True:
                params = {"page": page, "per_page": per_page}
                async with session.get(base_url, headers=headers, params=params) as response:
                    if response.status != 200:
                        return (response.status, await response.json(), await response.text())
    
                    page_data = await response.json()
                    if not page_data:  # No more results
                        break
    
                    all_commits.extend(page_data)
    
                    # If we got fewer results than per_page, we're done
                    if len(page_data) < per_page:
                        break
    
                    page += 1
    
            return (200, all_commits, "Success")
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries full burden. It states a read operation ('Get') but doesn't disclose behavioral traits like pagination, rate limits, authentication needs, or response format. For a tool with no annotation coverage, this leaves significant gaps in understanding how it behaves.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that front-loads the core purpose without unnecessary details. Every word contributes to understanding the tool's function, making it appropriately sized and well-structured.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no annotations, no output schema, and a read operation with one parameter, the description is incomplete. It lacks details on return values, error handling, or operational constraints, which are crucial for an agent to use the tool effectively in context.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, with the parameter 'merge_request_iid' fully documented in the schema. The description adds no additional meaning beyond implying the parameter's role in scoping discussions, matching the baseline for high schema coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb 'Get' and the resource 'discussions and comments on commits', specifying the scope 'within a specific merge request'. It distinguishes from siblings like 'get_merge_request_details' or 'get_merge_request_reviews' by focusing on commit-level discussions, but doesn't explicitly differentiate them.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No explicit guidance on when to use this tool versus alternatives like 'get_merge_request_reviews' or 'create_review_comment'. The description implies usage for commit discussions within merge requests, but lacks context on prerequisites, exclusions, or comparative use cases.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/amirsina-mandegari/gitlab-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server