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
filterargument to the merge requestdiscussionsGraphQL field, enabling system-note filtering and thread-based pagination) - Follow-up: #603389 (closed) (move remaining MCP tool queries into
.graphqlfiles) - Follow-up: #603096 (closed) (reorganize
Mcp::Toolsby domain, dropGraphqlprefix) - 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
- 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) - Fetch by project and IID:
tool.execute(params: { arguments: { project_id: 'group/project', merge_request_iid: 1 } }) - Fetch by URL instead (works for group/subgroup paths):
tool.execute(params: { arguments: { url: 'https://gdk.test:3443/group/project/-/merge_requests/1' } }) - Confirm
isErrorisfalseandstructuredContentcontainsresolvedDiscussionsCount,resolvableDiscussionsCount, and anotesconnection (pageInfo+count+nodes), with each node carrying adiscussion. - Check the error messages: a bad
project_idreturnsProject not found: …; a valid project with a missingmerge_request_iidreturnsMerge request not found: …. - 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=0example 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.