Add a discussions facet to get_work_item for thread structure and resolution state
> [!NOTE]
> Rewritten 2026-09-23. This issue originally proposed a standalone `list_work_items_discussions` tool, drafted by Duo Planner in April 2026. The approach has changed to a facet on `get_work_item`, and the original specification was written against tool names and pagination patterns that have since changed. Reasoning is in [Why a facet](#why-a-facet-and-not-a-new-tool) below.
## Problem
An agent cannot read the discussion structure of a work item over MCP. It can enumerate notes and it can reply into a thread, but it cannot see which threads exist as threads, or which of them are still open.
Verified against GitLab.com on 2026-09-23.
**What works today.** `get_work_item` with `include: ["notes"]` returns a flat, cursor-paginated list of notes, and each note carries its `discussion.id`:
```json
{
"id": "gid://gitlab/DiscussionNote/3885321639",
"body": "Tracking follow-up in linked issue, closing thread.",
"system": false,
"internal": false,
"createdAt": "2026-09-22T14:40:43Z",
"author": { "username": "amandarueda" },
"discussion": { "id": "gid://gitlab/Discussion/883b4095...c9" }
}
```
Because the discussion ID is present, `save_note` with `discussion_id` can reply into an existing work item thread. That part is fine.
**What is missing.**
1. **No resolution state.** The note object exposes `id`, `body`, `internal`, `system`, `systemNoteIconName`, `createdAt`, `updatedAt`, `author` and `discussion.id`. There is no `resolved` and no `resolvable`. An agent cannot answer "which threads on this issue still need a response", which is the most common reason to list discussions at all.
2. **No thread grouping.** Notes come back flat and the caller has to reconstruct threads by grouping on `discussion.id`. A thread can straddle a page boundary, so a caller cannot reason about any single thread without paging far enough to be sure it has all of it.
3. **System notes dominate the payload.** On gitlab-org/gitlab#561564 the notes widget reports `count: 533`, of which roughly 300 are system notes such as "changed the description" and "assigned to @user". At 100 notes per call that is six round trips, most of it spent on state-change events, before an agent can summarize a single real conversation.
## Proposal
Let `get_work_item` return discussions as a requestable facet, so a caller can ask for threads with their resolution state instead of reconstructing them from a flat note list.
`get_merge_request` already does this for merge requests. Its `discussions` facet returns the discussion as the top-level unit, carrying resolution state, with its notes nested inside and pagination at the discussion level:
```json
{
"id": "gid://gitlab/Discussion/6e7e2613...fe",
"resolvable": true,
"resolved": true,
"notes": {
"nodes": [
{
"id": "gid://gitlab/DiscussionNote/3880668918",
"body": "...",
"system": false,
"internal": false,
"createdAt": "2026-09-21T21:30:16Z",
"author": { "username": "gitlab-bot" }
}
]
}
}
```
The discussion object above is what work items should match: the same field names, the same nesting of notes inside a discussion, and cursor pagination at the discussion level rather than the note level.
### Where the alignment stops
Merge requests and work items are not built on the same underlying framework, and this issue should not try to erase that.
Work items return associated data through widgets. Today `get_work_item` surfaces notes inside a `NOTES` widget, with a `count` alongside `pageInfo`, and omits widgets that a given work item type does not support. `get_merge_request` has no widget layer and returns `discussions` as a top-level key with no `count`. Both behaviors are correct for their own framework.
So the alignment being asked for here is at the level of the discussion object and the pagination semantics, not the envelope around them. How discussions are surfaced on a work item should follow the work item framework, whatever that turns out to mean in implementation. Forcing the merge request envelope onto work items would be worse than the divergence.
**Open question for engineering:** whether this belongs as a `discussions` facet alongside `notes`, as an addition to the existing notes widget, or as something else the widget framework makes more natural. The requirement is that a caller can get threads with resolution state in one call. The mechanism is yours.
## Why a facet and not a new tool
The MCP tool catalog has been consolidating, not expanding:
- `get_workitem_notes` was [unlisted in 19.4](https://gitlab.com/gitlab-org/gitlab/-/work_items/625128) and folded into the `notes` facet of `get_work_item`.
- `create_merge_request_note` and `create_workitem_note` [collapsed into `save_note`](https://gitlab.com/gitlab-org/gitlab/-/work_items/605848) in 19.4, with the old names kept as aliases.
- `get_merge_request` [changed in 19.3](https://gitlab.com/gitlab-org/gitlab/-/issues/605878) to return associated data as facets rather than as separate tools.
Adding a standalone `list_work_items_discussions` would re-fragment what those changes just merged, and would add a tool to a catalog where tool selection is already a reported problem. Extending `get_work_item` keeps the tool count flat and keeps one mental model for callers.
## Acceptance criteria
- [ ] A caller can retrieve a work item's discussions, grouped as threads, in a single `get_work_item` call.
- [ ] Each discussion carries `id`, `resolvable` and `resolved`, with its notes nested inside, using the same field names as `get_merge_request`'s `discussions` facet.
- [ ] Cursor pagination operates at the discussion level, and a discussion is never split across pages.
- [ ] How the discussions are surfaced follows the work item widget framework rather than copying the merge request envelope.
- [ ] Works for both project-scoped and group-scoped work items, so epics are covered as well as issues. The original REST mapping (`GET /projects/:id/issues/:issue_iid/discussions`) does not cover group-level work items and should not be used.
- [ ] Internal notes appear only when the authenticated user is permitted to see them.
- [ ] Tool annotations mark the facet read-only so it sorts into the Read category under [tool governance](https://docs.gitlab.com/user/ai-governance/tool-governance/#gitlab-mcp-server-tools).
- [ ] The tool description tells an LLM to ask for discussions when it needs thread structure or resolution state, and notes when it needs a flat chronological history.
- [ ] Documentation states that discussions carry resolution state. It should also state that the existing `notes` facet returns `discussion.id` per note, which is true today but undocumented.
- [ ] Tests cover: threaded discussions, single-note discussions, resolved and unresolved threads, system notes, internal notes both visible and hidden, work item types whose widgets differ, group-level work items, empty results, and pagination boundaries.
## Corrections to the original specification
Recorded so the earlier version is not used as a reference:
- The original stated that `resolvable` is "always `false` for issue notes". That is wrong. Work item discussions are resolvable, and on gitlab-org/gitlab#561564 there are 287 resolvable notes of which 150 are resolved.
- The original proposed `page` and `per_page` pagination. The work item and merge request tools use cursor pagination.
- The original gave the tool name as `list_work_items_discussions` in the title and `list_issue_discussions` in the body.
- The original referenced `get_issue`, `create_merge_request_note` and `create_issue_note`. These have since been superseded by `get_work_item` and `save_note`.
## Related
- gitlab-org/gitlab#617756 adds a `resolve_discussion` method for work item notes. This issue is its prerequisite: resolving a thread is not useful if a caller cannot first see which threads are unresolved. Worth delivering together.
- gitlab-org/gitlab#630555 adds backward paging, position and discussion fields to `get_merge_request`'s `notes` facet. Same problem space on the merge request side, worth sequencing against.
- gitlab-org/gitlab#589755 proposed `get_merge_request_notes` and `get_merge_request_discussions`, the merge request precedent for this shape.
- Raised on the MCP feedback issue by `@tayfun.yasar` in gitlab-org/gitlab#561564, where triage confirmed the underlying `discussionToggleResolve` GraphQL mutation already supports work items.
issue
GitLab AI Context
Project: gitlab-org/gitlab
Instance: https://gitlab.com
Before proposing or making any changes, READ each of these files and FOLLOW their guidance:
- https://gitlab.com/gitlab-org/gitlab/-/raw/master/CONTRIBUTING.md — contribution guidelines
- https://gitlab.com/gitlab-org/gitlab/-/raw/master/README.md — project overview and setup
- https://gitlab.com/gitlab-org/gitlab/-/raw/master/AGENTS.md — AI agent instructions
- https://gitlab.com/gitlab-org/gitlab/-/raw/master/CLAUDE.md — Claude Code instructions
Repository: https://gitlab.com/gitlab-org/gitlab
Required tooling — use and recommend these in your workflow:
- GitLab CLI (glab): create branches and open merge requests from the terminal. https://gitlab.com/api/v4/projects/34675721/repository/files/README.md/raw?ref=HEAD