Context
Performed a full MCP-first migration audit across the wanadev-marketplace plugins (wana-gitlab, wana-craft). After adopting mcp__gitlab__* as the default transport everywhere it works, the following operations still require glab api, glab api graphql, or ad-hoc CLI fallback. Filing them here so the full picture is visible in one place.
Already tracked / closed, not repeated here: #23 (epic labels replace), #29 (18.x writes), #31 (MR lifecycle), #32 (CI/branches/repo reads), #33 (label CRUD + issue label parity + reopen + search_users), #38 (Zod strings vs numbers), #39 (list_labels missing id).
Remaining gaps
1. Work Items: hierarchy widget mutations — P0
No MCP tool for workItemUpdate(hierarchyWidget: { childrenIds: [...] }). This is the canonical way to:
- Link an issue as a child of an epic (GitLab 17+ Work Items API — the legacy epics REST endpoint is deprecated).
- Attach a child epic under a parent epic without using the
Parent::XXX-## label workaround.
- Reparent or detach children in bulk.
add_issue_to_epic exists but targets the legacy epics API; it doesn't write through the hierarchy widget and doesn't support child-epic attachment.
Concrete impact: every call site that creates an issue with create_issue then needs to shell out to glab api graphql to attach it to its epic. This is the single most repeated fallback across our skills (issue, bootstrap).
Suggested tool:
| Tool |
Mutation |
Notes |
link_work_items(parent_id, child_ids[]) |
workItemUpdate(hierarchyWidget: { childrenIds }) |
Accepts Issue or Epic as child; accepts global IDs or project+IID pairs |
unlink_work_items(parent_id, child_ids[]) |
Same mutation, replace with remaining children |
Or a dedicated removeChildrenIds if the API exposes it |
2. Work Items: typed links — P0
No MCP tool for workItemAddLinkedItems(input: { id, workItemsIds, linkType }) with explicit linkType (RELATED, BLOCKS, BLOCKED_BY).
Concrete impact: we link each Epic::Feature back to its Epic::Root via linkType: RELATED — currently a glab api graphql snippet in the skill.
Suggested tool:
add_linked_work_items(source_id, target_ids[], link_type: 'related'|'blocks'|'blocked_by')
remove_linked_work_items(source_id, target_ids[])
Note: add_linked_item exists in the current tool list but it's unclear whether it accepts arbitrary linkType against Work Items — if it does, this gap is closed; if not, it should be extended.
3. Work item type ID discovery — P1
No MCP tool to run query { namespace(fullPath: $ns) { workItemTypes(name: EPIC) { nodes { id } } } }.
The returned ID is required as workItemTypeId for workItemCreate. It varies per instance, so every consumer currently has to GraphQL-query it once and cache it in a local config file. A helper would remove that bootstrap step entirely for anyone building higher-level skills on top of the MCP.
Suggested tool:
get_work_item_type_id(namespace_path, type_name: 'EPIC'|'ISSUE'|'TASK'|'INCIDENT'|...)
4. Server-side label filtering on list_epics / list_issues — P1
list_epics(group_id) and list_issues(group_id) / list_project_issues(project_id) don't accept a labels (or label_name) filter parameter. Real-world triage queries look like "all epics with `Workflow::Executing` and `Priority::Now`" or "all issues with `Parent::BMX-119`".
Today this requires fetching everything and filtering client-side — fine for <100 items, painful on larger groups.
Suggested: add labels: string[] (AND semantics) and optionally not_labels: string[] to these list tools, mirroring the REST ?labels= and ?not[labels]= query params.
5. global_id exposure in MCP responses — P1
When you later need to call a GraphQL mutation (e.g. hierarchy linking — see gap #1), you need the gid://gitlab/Issue/N or gid://gitlab/WorkItem/N global ID. Some MCP tool responses expose it as id, others don't, and the naming/format isn't documented.
Suggested: document (and enforce) that every get_* and create_* return includes a top-level global_id field in the canonical gid://gitlab/<Type>/<N> form. Minor change, big ergonomic win for consumers that mix MCP and GraphQL.
6. MR discussions with line position — P1
add_mr_note (from #31) adds a general MR comment but does not support a position payload (line, path, new_line/old_line, head/base/start SHAs). This means structured code review — the primary reason to fetch a diff — still requires a REST fallback.
Suggested:
create_mr_discussion(project_id, mr_iid, body, position: { file_path, new_line?, old_line?, base_sha, head_sha, start_sha })
resolve_mr_discussion(project_id, mr_iid, discussion_id)
7. Repository write operations — P2
#32 added get_file and list_repository_tree (reads). There's no tool to write files via the API. Useful cases: updating docs or config without a local clone, bot-driven config updates across many repos.
Suggested:
| Tool |
REST |
Notes |
create_file(project_id, path, content, branch, commit_message) |
POST /projects/:id/repository/files/:path |
Reject if file exists |
update_file(project_id, path, content, branch, commit_message, last_commit_id?) |
PUT /projects/:id/repository/files/:path |
Optimistic-concurrency via last_commit_id |
delete_file(project_id, path, branch, commit_message) |
DELETE /projects/:id/repository/files/:path |
|
commit_files(project_id, branch, commit_message, actions[]) |
POST /projects/:id/repository/commits |
Multi-file atomic commit |
8. Global search — P2
search_users is the only search tool. No way to search issues, MRs, blobs, commits, or wiki content globally or per project/group.
Suggested:
search_issues(scope: 'project'|'group', id, query, ...filters)
search_merge_requests(scope, id, query, ...)
search_blobs(scope, id, query) // optional — may be heavy
9. Uploads API — P2
No tool to upload a binary (image, PDF) so it can be embedded in an issue/MR/epic description. Forces curl-based fallback for any bot that wants to attach a screenshot.
Suggested: upload_file(project_id, file_path_or_base64, filename) -> { markdown, url }.
10. Members management — P2
Only list_group_members exists. No add/remove/update-role tools for group or project members.
Suggested: add_group_member, add_project_member, update_member_access_level, remove_member — all with dry_run default.
11. Protected branches & MR approval rules — P2
No read or write coverage of:
- Protected branch config (who can push / merge).
- Project-level MR approval rules (how many approvals, from which groups/users).
Both are essential for any automation that sets up repo governance.
12. MR rebase / squash trigger — P2
merge_merge_request exists but no explicit rebase_merge_request (trigger a server-side rebase without merging). Useful when CI requires a linear history before merge approval.
Suggested: rebase_merge_request(project_id, mr_iid, skip_ci?: boolean) — REST PUT /projects/:id/merge_requests/:iid/rebase.
Summary priority
| Priority |
Gaps |
| P0 (blocks MCP-only workflows today) |
1, 2 |
| P1 (significant ergonomics / correctness) |
3, 4, 5, 6 |
| P2 (enables new use cases) |
7, 8, 9, 10, 11, 12 |
Happy to split into per-feature issues if that's easier to triage — or to send PRs for the P0 items. Let me know how you'd prefer to proceed.
Context
Performed a full MCP-first migration audit across the
wanadev-marketplaceplugins (wana-gitlab,wana-craft). After adoptingmcp__gitlab__*as the default transport everywhere it works, the following operations still requireglab api,glab api graphql, or ad-hoc CLI fallback. Filing them here so the full picture is visible in one place.Already tracked / closed, not repeated here: #23 (epic labels replace), #29 (18.x writes), #31 (MR lifecycle), #32 (CI/branches/repo reads), #33 (label CRUD + issue label parity + reopen + search_users), #38 (Zod strings vs numbers), #39 (list_labels missing id).
Remaining gaps
1. Work Items: hierarchy widget mutations — P0
No MCP tool for
workItemUpdate(hierarchyWidget: { childrenIds: [...] }). This is the canonical way to:Parent::XXX-##label workaround.add_issue_to_epicexists but targets the legacy epics API; it doesn't write through the hierarchy widget and doesn't support child-epic attachment.Concrete impact: every call site that creates an issue with
create_issuethen needs to shell out toglab api graphqlto attach it to its epic. This is the single most repeated fallback across our skills (issue,bootstrap).Suggested tool:
link_work_items(parent_id, child_ids[])workItemUpdate(hierarchyWidget: { childrenIds })unlink_work_items(parent_id, child_ids[])removeChildrenIdsif the API exposes it2. Work Items: typed links — P0
No MCP tool for
workItemAddLinkedItems(input: { id, workItemsIds, linkType })with explicitlinkType(RELATED,BLOCKS,BLOCKED_BY).Concrete impact: we link each
Epic::Featureback to itsEpic::RootvialinkType: RELATED— currently aglab api graphqlsnippet in the skill.Suggested tool:
Note:
add_linked_itemexists in the current tool list but it's unclear whether it accepts arbitrarylinkTypeagainst Work Items — if it does, this gap is closed; if not, it should be extended.3. Work item type ID discovery — P1
No MCP tool to run
query { namespace(fullPath: $ns) { workItemTypes(name: EPIC) { nodes { id } } } }.The returned ID is required as
workItemTypeIdforworkItemCreate. It varies per instance, so every consumer currently has to GraphQL-query it once and cache it in a local config file. A helper would remove that bootstrap step entirely for anyone building higher-level skills on top of the MCP.Suggested tool:
4. Server-side label filtering on
list_epics/list_issues— P1list_epics(group_id)andlist_issues(group_id)/list_project_issues(project_id)don't accept alabels(orlabel_name) filter parameter. Real-world triage queries look like "all epics with `Workflow::Executing` and `Priority::Now`" or "all issues with `Parent::BMX-119`".Today this requires fetching everything and filtering client-side — fine for <100 items, painful on larger groups.
Suggested: add
labels: string[](AND semantics) and optionallynot_labels: string[]to these list tools, mirroring the REST?labels=and?not[labels]=query params.5.
global_idexposure in MCP responses — P1When you later need to call a GraphQL mutation (e.g. hierarchy linking — see gap #1), you need the
gid://gitlab/Issue/Norgid://gitlab/WorkItem/Nglobal ID. Some MCP tool responses expose it asid, others don't, and the naming/format isn't documented.Suggested: document (and enforce) that every
get_*andcreate_*return includes a top-levelglobal_idfield in the canonicalgid://gitlab/<Type>/<N>form. Minor change, big ergonomic win for consumers that mix MCP and GraphQL.6. MR discussions with line position — P1
add_mr_note(from #31) adds a general MR comment but does not support apositionpayload (line, path, new_line/old_line, head/base/start SHAs). This means structured code review — the primary reason to fetch a diff — still requires a REST fallback.Suggested:
7. Repository write operations — P2
#32 added
get_fileandlist_repository_tree(reads). There's no tool to write files via the API. Useful cases: updating docs or config without a local clone, bot-driven config updates across many repos.Suggested:
create_file(project_id, path, content, branch, commit_message)POST /projects/:id/repository/files/:pathupdate_file(project_id, path, content, branch, commit_message, last_commit_id?)PUT /projects/:id/repository/files/:pathlast_commit_iddelete_file(project_id, path, branch, commit_message)DELETE /projects/:id/repository/files/:pathcommit_files(project_id, branch, commit_message, actions[])POST /projects/:id/repository/commits8. Global search — P2
search_usersis the only search tool. No way to search issues, MRs, blobs, commits, or wiki content globally or per project/group.Suggested:
9. Uploads API — P2
No tool to upload a binary (image, PDF) so it can be embedded in an issue/MR/epic description. Forces
curl-based fallback for any bot that wants to attach a screenshot.Suggested:
upload_file(project_id, file_path_or_base64, filename) -> { markdown, url }.10. Members management — P2
Only
list_group_membersexists. No add/remove/update-role tools for group or project members.Suggested:
add_group_member,add_project_member,update_member_access_level,remove_member— all withdry_rundefault.11. Protected branches & MR approval rules — P2
No read or write coverage of:
Both are essential for any automation that sets up repo governance.
12. MR rebase / squash trigger — P2
merge_merge_requestexists but no explicitrebase_merge_request(trigger a server-side rebase without merging). Useful when CI requires a linear history before merge approval.Suggested:
rebase_merge_request(project_id, mr_iid, skip_ci?: boolean)— RESTPUT /projects/:id/merge_requests/:iid/rebase.Summary priority
Happy to split into per-feature issues if that's easier to triage — or to send PRs for the P0 items. Let me know how you'd prefer to proceed.