Implementation Guide

    How to Block AI Agents from Merging GitLab Merge Requests Through MCP

    Treat the merge as a layered control problem. Scope the agent's GitLab credential to least privilege, protect target branches and require approvals in GitLab, then enforce policy on MCP tool calls in the execution path. Policy allows read, comment, and open-merge-request calls, and blocks or escalates any merge call for human approval before it reaches GitLab. Retain a record of each decision as evidence.

    Where native controls fall short in an MCP workflow

    1. 1

      Token scopes

      Coarse. Narrowing a token far enough to prevent merging can also remove legitimate actions the agent needs.

    2. 2

      Protected branches

      Stop merges to specific branches only. They do not express a rule about which caller or tool may attempt the action.

    3. 3

      Approval rules

      Govern whether a merge is allowed to complete. They do not necessarily distinguish an agent-initiated merge from a human one, and they do not record why a tool call was made.

    4. 4

      Service account roles

      Help with attribution, but a role is static. It cannot weigh the context of an individual call.

    Step three: verify that enforcement works

    • With policy disabled in a test project, confirm the agent identity's role cannot merge into a protected branch.
    • With policy enabled, call the merge tool and confirm the call is blocked or held before any request reaches GitLab.
    • Confirm read, comment, and open-merge-request calls still succeed under the same identity.
    • Trigger an escalation, approve it as a named human, and confirm the merge completes only after that decision.
    • Reject an escalation and confirm no merge occurs and the rejection is recorded.
    • Re-run these tests after any change to the MCP server, its tool list, or the agent's credential.

    How an MCP-connected agent reaches the merge operation

    An MCP server publishes a list of tools. The agent reads that list, interprets a natural-language task, and chooses which tool to call. The server then acts on GitLab using a credential it was configured with, typically a personal access token or an OAuth grant. If the server exposes a tool that merges a merge request, the only thing between the model's choice and the merge is whatever limits sit on that credential.

    GitLab sees the identity behind the credential, not the intent behind the call. A merge triggered by an agent using a developer's token can look the same as a merge made by that developer. NIST guidance on agentic security makes the underlying point: agents pick tools without strong constraints, so they should not be given broad privileges or open-ended tools, and they should not run without telemetry.

    The first task is therefore an inventory. For each GitLab MCP server your teams run, list the tools it exposes, mark which ones change repository state, and record which credential each agent inherits. Use the server's own documentation and repository for this, since official and community servers differ in what they expose.

    Step one: constrain the credential and the branch in GitLab

    Start with native controls. They cost little, they apply regardless of which client or agent is connected, and they remain the backstop if a runtime layer is misconfigured. NCCoE functional demonstration results show GitLab protected branches, merge rules, push rules, and security policies being used to ensure that code that has not been approved cannot be merged into main.

    The aim is that a credential held by an agent could not complete a merge even if the tool call were made.

    • Use a dedicated identity for each agent: Avoid reusing a developer's personal token. A separate identity makes agent activity attributable and lets you set its role independently.
    • Grant the lowest role that supports the work: Reading, commenting, and opening merge requests should not require the ability to merge into protected branches.
    • Protect target branches: Set merge and push access levels so the agent identity is not permitted to merge into main or release branches.
    • Require approvals: Configure merge request approval rules so unapproved code cannot merge, and keep approval rights with authorized humans.
    • Shorten credential lifetime: Prefer expiring tokens with narrow scopes, and rotate them on a schedule.

    Step two: enforce policy on the MCP tool call

    The remaining gap is the call itself. Place an enforcement point in the execution path between the agent and the MCP server, so each tool call is evaluated before it reaches GitLab. This follows the boundary model described in NIST material on runtime execution governance: evaluate policy, then commit or block. That is different from warning, scoring, or logging, which describe an action after it has happened.

    Policy first has to recognize the call. A tool call carries the tool name, the arguments passed to it, and the identity of the caller. A rule can match on the merge tool by name, and where arguments are available it can also consider the project, the merge request, or the target branch. Keep the rule set small and explicit: allow read, comment, and open-merge-request tools, block merge tools by default, and treat any tool not yet classified as denied until reviewed.

    Two outcomes are worth separating for merges. Block is right where no agent should ever merge, such as production repositories. Escalate is right where a merge is sometimes legitimate: the call is held, a human reviews it, and only an approval releases it. NCCoE material describes this division of labor, with humans defining guardrails and adjudicating high-consequence exceptions. Where policy is enforced is also a design choice. Keeping the decision outside the agent's own credential means the agent cannot change the rule that constrains it.

    Trussed enforces policy on tool calls and MCP servers in the execution path, with allow, block, and escalate-for-human-approval decisions at the level of an individual tool call, enforced at runtime, not reviewed after the fact.

    What evidence to retain

    A security team should be able to show three things: that a merge attempt by an agent was stopped, that an allowed action was within policy, and that an escalated merge was approved by an identifiable person. That requires a record per decision rather than a general activity log.

    For each tool call, retain the timestamp, the agent and credential identity, the MCP server and tool name, the relevant arguments such as project and target branch, the policy and version that applied, and the decision. For escalations, add the approver's identity, the time of the decision, and a link between the approval and the specific call it released. Also keep the GitLab-side configuration: protected branch settings, approval rules, and the agent's role, captured at a point in time.

    These records support change-management expectations because they show who or what requested a change, what rule applied, and who authorized an exception. They also fit NIST's guidance on telemetry for agents. When mapping to the NIST AI RMF, align each record type to the categories your own program uses. Trussed produces audit-ready evidence from enforcement itself, mapped to NIST and other frameworks, with configurable retention and self-managed deployment in the customer cloud.

    Layered control for agent merges

    Credential

    Least-privilege token or service account held by the agent

    GitLab rules

    Protected branches and approval rules on target branches

    Tool-call policy

    Allow, block, or escalate each MCP call before it reaches GitLab

    Evidence

    Decision records for blocked, allowed, and escalated calls

    See how tool-call enforcement applies to your GitLab agents

    Review how allow, block, and escalate decisions on MCP tool calls produce audit-ready evidence for merge control.

    Request a Demo
    Next step

    How does your AI governance program compare?

    See where your program has gaps in less than 2 minutes.