Treat zero work_item_iid as create in save_work_item MCP tool
What does this MR do and why?
Fixes the save_work_item MCP tool's create/update dispatch for clients that zero-fill their request payloads. Some agent executors (Go-style serializers) send every schema field with its zero value, including work_item_iid: 0 on create requests. Rails presence treats the integer 0 as present, so the tool routed these create requests down the update path, which then failed opaquely against a work item that cannot exist (iids start at 1). In the reported incident, the agent fell back to the legacy create work item / update work item tools instead.
Closes #621661 (closed) (GA must-have, type::bug).
The dispatch check now treats work_item_iid as an update signal only when it is a positive integer. 0 is unambiguous here: iids start at 1, so a zero value can only mean the client left the field unset.
The tool description and the work_item_iid parameter description now warn agents to send only the fields they intend to set, and to never send placeholder zero values (0, empty strings, empty arrays). doc/user/model_context_protocol/mcp_server_tools.md is updated accordingly.
Design notes
- The framework already treats
niland empty-string arguments as omitted:Mcp::Tools::Base::BaseService#reject_omitted_argumentsstrips them before dispatch. This change extends the same "omitted value" policy to the one remaining unambiguous zero value, integer0forwork_item_iid. - Deliberately not scrubbing empty arrays or booleans: on the update path,
assignee_ids: []legitimately means "clear all assignees," andfalseis a real boolean value, not a placeholder. Presence-based semantics have to be preserved for those types even though they're relaxed forwork_item_iid. - A fully zero-filled request that gets routed to the create path can still fail if it carries update-only fields with non-zero values (for example
state: "opened"). It now fails with the create path's self-correcting message ("state can only be used when updating (provide work_item_iid)") instead of an opaque not-found from the update path, so agents can drop the offending field and retry successfully.
How to validate locally
- Check out the branch and restart GDK rails (
gdk restart rails-web). - Create a personal access token with
apiandmcpscopes. - Call
tools/callonsave_work_itemwithwork_item_iid: 0plus clean create fields (project_id,title,type_name) — a work item is created. - Replay the incident-shaped zero-filled payload — the call fails on the create path with the self-correcting message about update-only fields, instead of the update path's opaque failure.
- Call with a positive
work_item_iid— the work item is updated (regression check).
The following is the transcript of the end-to-end run against GDK used to validate this change:
1a. description has zero-value warning: true
1b. iid description: "Positive internal ID of the work item to update. Omit to create a new work item."
2. incident shape (expect create-path self-correcting error): isError=true "Validation error: state can only be used when updating (provide work_item_iid)"
3. zero iid clean create: isError=false {"id"=>"gid://gitlab/WorkItem/806", "iid"=>"27", "type"=>"Issue", "title"=>"E2E 621661 clean create", "state"=>"OPEN", ...}
db: created iid=27 title="E2E 621661 clean create"
4. positive iid update: isError=false {"id"=>"gid://gitlab/WorkItem/806", "iid"=>"27", "type"=>"Issue", "title"=>"E2E 621661 updated", ...}
db title now: "E2E 621661 updated"