Accept plain iids for link_work_items MCP tool targets
What does this MR do and why?
The link_work_items MCP tool required two different ID formats in the same call: the source work item is identified by project_id/group_id + work_item_iid (a plain integer), but work_items_ids (the targets) had to be global IDs (gid://gitlab/WorkItem/<id>). Mixing formats confused agents, and a failed lookup returned a single generic "No matching work item found" error that didn't say which id was the problem.
This MR lets work_items_ids accept plain iids (integers or numeric strings) alongside global IDs, and makes resolution errors name the failing id. Cross-project and cross-group targets still require global IDs, per the issue's scoping discussion.
Changes:
- An iid in
work_items_idsresolves in the source work item's project or group, i.e. the same scope already used to resolvework_item_iid. - An unresolvable iid returns:
Target work item with iid '<value>' not found in <parent path>. Use a global ID (gid://gitlab/WorkItem/<id>) for work items in other projects or groups. - An unrecognized id format returns:
Invalid target work item ID format: '<value>'. Expected an iid (integer) or a global ID (gid://gitlab/WorkItem/<id>). - Widened the
items.typeinput schema to["integer", "string"]and updated the schema and tool descriptions indoc/user/model_context_protocol/mcp_server_tools.md. - No GraphQL changes: the tool still calls
workItemAddLinkedItemswith resolved global IDs.
The numeric-id normalization approach follows the one already used in !250475 (merged).
References
- Closes #618676 (closed)
How to verify locally
bundle exec rubocop— clean.- Run the CE and EE specs for the
link_work_itemstool and its service (54 examples, all green). - Against a running GDK, call
POST /api/v4/mcpwith alink_work_itemsJSON-RPC request:- Mix one plain iid and one global ID in
work_items_ids— both links should be created (IssueLinkrecords in the database). - Pass an iid that doesn't exist in the source's project/group — expect the parent-scoped "not found" error naming that iid.
- Pass a malformed value (e.g. a non-numeric string) — expect the format error naming both accepted id forms.
- Mix one plain iid and one global ID in