Add get_merge_request_notes MCP tool

What does this MR do and why?

Adds a get_merge_request_notes MCP tool that returns a merge request's notes (comments and system notes) to MCP clients. It accepts either a url or a project_id + merge_request_iid, and paginates over the flat notes connection the same way get_work_item_notes does. Each note carries its discussion (id plus resolution state) so an agent can group threads and spot unresolved ones, and the response also includes resolvedDiscussionsCount / resolvableDiscussionsCount for an at-a-glance summary.

Design notes — query shape, naming, follow-ups

Output shape: the tool queries the flat notes connection on the merge request (mirroring get_work_item_notes) and returns the merge request with the two resolution counts plus the paginated notes connection. Pagination is over notes, so each note includes its discussion { id } for grouping. Filtering system notes server-side needs a change to the merge request discussions GraphQL field and is deferred (see follow-up below).

Query location: the GraphQL document lives in app/graphql/queries/mcp/merge_requests/get_merge_request_notes.query.graphql rather than an inline heredoc, so spec/graphql/all_queries_spec.rb validates it against GitlabSchema in CI. The load_graphql helper added to GraphqlTool makes this reusable for future tools.

Service naming: the new service is GetMergeRequestNotesService — deliberately without the Graphql prefix the existing GRAPHQL_TOOLS services carry. The prefix carries no signal: tools group by domain, the paired Tool class is unprefixed, and the class already inherits from GraphqlService.

The CE and EE tools/list request specs are updated to include the new tool.

References

  • Related to #597494 (closed) (the issue also covers create_merge_request_note; this MR delivers the read tool only)
  • Follow-up: #603390 (add a filter argument to the merge request discussions GraphQL field, enabling system-note filtering and thread-based pagination)
  • Follow-up: #603389 (closed) (move remaining MCP tool queries into .graphql files)
  • Follow-up: #603096 (closed) (reorganize Mcp::Tools by domain, drop Graphql prefix)
  • Follow-up: #603097 (generate the MCP tools reference doc from tool metadata)

Screenshots or screen recordings

No UI changes.

How to set up and validate locally

  1. In a Rails console (or bin/rails runner), fetch the tool and set credentials:
    tool = Mcp::Tools::Manager.new.get_tool(name: 'get_merge_request_notes')
    tool.set_cred(current_user: User.find_by_username('root'), access_token: nil)
  2. Fetch by project and IID:
    tool.execute(params: { arguments: { project_id: 'group/project', merge_request_iid: 1 } })
  3. Fetch by URL instead (works for group/subgroup paths):
    tool.execute(params: { arguments: { url: 'https://gdk.test:3443/group/project/-/merge_requests/1' } })
  4. Confirm isError is false and structuredContent contains resolvedDiscussionsCount, resolvableDiscussionsCount, and a notes connection (pageInfo + count + nodes), with each node carrying a discussion.
  5. Check the error messages: a bad project_id returns Project not found: …; a valid project with a missing merge_request_iid returns Merge request not found: ….
  6. Run the specs:
    bundle exec rspec spec/services/mcp/tools/merge_requests/ spec/requests/api/mcp/handlers/list_tools_spec.rb ee/spec/requests/api/mcp/handlers/list_tools_spec.rb
    bundle exec rspec spec/graphql/all_queries_spec.rb -e 'mcp/merge_requests/get_merge_request_notes'

using @modelcontextprotocol/inspector

npx -y @modelcontextprotocol/inspector mise "x --"  npx -y mcp-remote "https://gdk.test:3443/api/v4/mcp" --debug -e NODE_TLS_REJECT_UNAUTHORIZED=0
example output (real output from a GDK merge request, truncated to two notes)
{
  "resolvedDiscussionsCount": 0,
  "resolvableDiscussionsCount": 3,
  "notes": {
    "pageInfo": {
      "hasNextPage": false,
      "hasPreviousPage": false,
      "startCursor": "eyJjcmVhdGVkX2F0IjoiMjAyNi0wNi0xMCAxNzoyNjo1NS4wODkwNDUwMDAgKzAwMDAiLCJpZCI6IjE1NDQifQ",
      "endCursor": "eyJjcmVhdGVkX2F0IjoiMjAyNi0wNi0xNiAxMzoxMTozNi40MzE1MTMwMDAgKzAwMDAiLCJpZCI6IjE1NDgifQ"
    },
    "count": 5,
    "nodes": [
      {
        "id": "gid://gitlab/DiscussionNote/1544",
        "webUrl": "https://gdk.test:3443/top-level-public/sub-group-internal/sub-group-private/sub-project-private/-/merge_requests/1#note_1544",
        "body": "test 1",
        "system": false,
        "internal": false,
        "createdAt": "2026-06-10T17:26:55Z",
        "updatedAt": "2026-06-10T17:26:59Z",
        "author": {
          "id": "gid://gitlab/User/1",
          "username": "root",
          "name": "Administrator"
        },
        "position": null,
        "discussion": {
          "id": "gid://gitlab/Discussion/16332cf01fa2c27f4b5d1abf29ffa2f740da78fc",
          "resolvable": true,
          "resolved": false,
          "resolvedBy": null
        }
      },
      {
        "id": "gid://gitlab/DiffNote/1548",
        "webUrl": "https://gdk.test:3443/top-level-public/sub-group-internal/sub-group-private/sub-project-private/-/merge_requests/1#note_1548",
        "body": "```suggestion:-1+0\n# Test\n\nAdd more data here\n\nThis is not enough\n```",
        "system": false,
        "internal": false,
        "createdAt": "2026-06-16T13:11:36Z",
        "updatedAt": "2026-06-16T13:11:36Z",
        "author": {
          "id": "gid://gitlab/User/1",
          "username": "root",
          "name": "Administrator"
        },
        "position": {
          "newPath": "test.md",
          "oldPath": "test.md",
          "newLine": 2,
          "oldLine": null
        },
        "discussion": {
          "id": "gid://gitlab/Discussion/6ec8153959f587b9713de1f3c5de6987a0e852e4",
          "resolvable": true,
          "resolved": false,
          "resolvedBy": null
        }
      }
    ]
  }
}

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Terri Chu

Merge request reports

Loading
Loading