Skip to content

Gap audit: remaining operations still requiring glab/GraphQL fallback #40

@Hakihiro

Description

@Hakihiro

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_issuesP1

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions