Over the past sprint, I contributed a series of upstream enhancements and bug fixes to GitLab (gitlab-org/gitlab) and the GitLab Language Server ( gitlab-org/editor-extensions/gitlab-lsp).
Most of these changes focus on GitLab’s implementation of the Model Context Protocol (MCP)—the protocol connecting AI agents and IDE assistants (like GitLab Duo) to GitLab's API—as well as race-condition resilience in Language Server protocols.
Here is a comprehensive retrospective on what was built, the architectural decisions behind them, and lessons learned getting complex merge requests to 100% green pipelines under GitLab's rigorous review standards.
save_note Tool (!264854)
The MCP save_note tool allowed AI agents to post comments, reply to discussion threads, and resolve discussions on merge requests and work items. However, agents could not edit their existing comments or refine feedback in-place, leading to comment sprawl.
We introduced the update_note method to save_note:
Mutations::Notes::Update::Base (updateNote) with proper authorization checks and quick-action guards. gid://gitlab/Note/123). We updated the tool schema to accept oneOf: [{ type: 'string' }, { type: 'integer' }] and automatically coerce them.project, so we ensured the governance container resolver properly fell back to note.namespace to enforce project/group tool policies.false.blank? Trap:true. When validating method arguments for boolean fields like resolved: false, a naive .blank? check erroneously flagged the parameter as missing. We implemented strict string-blankness checking (val.nil? || (val.is_a?(String) && val.blank?)) to guarantee boolean stability.
Agents needed visibility into recent user activity to assist with sprint summaries and standups. While /users/:id/events existed as a REST endpoint, it was not exposed as an MCP tool.
list_user_events in the :core toolset with pagination, action, and target-type filters.ApiTool::TYPE_CONVERSIONS): before and after with type: Date. Grape's MCP tool mapper previously lacked a mapping for Date, emitting "type": "date" in the generated JSON Schema—which is invalid according to the JSON Schema meta-schema and caused strict MCP clients to reject the tool catalog. We added 'Date' => 'string' to map dates cleanly to ISO 8601 strings.mcp_server_tools.md. save_pipeline (!264858)
An earlier draft introduced a separate tool to create merge request pipelines. Reviewers pointed out that having both save_pipeline and a dedicated merge request pipeline tool introduced fragmentation and API redundancy.
SavePipelineService using an optional merge_request_iid parameter.parent_iid support to the list_work_items MCP tool, enabling agents to query tasks and child items belonging to a specific parent issue or epic.gitlab-lsp (!4539)
In the GitLab Language Server (gitlab-lsp), when a user ran /new to initiate a clean session while pending wake messages were queued, old microtasks could resolve and dispatch messages to the newly instantiated session, causing stale state corruption.
await flushMicrotasks() during session teardown.false.blank? == true. Always check .nil? or type-check when accepting booleans."date" will break tool discovery across ecosystems.
All Merge Requests linked above are open or merged in GitLab's official repositories. Happy hacking!