Add MCP tool for retrieving merge request conflict data

Summary

Implements Phase 1 of the Conflict Resolver Agent:

This adds a new MCP tool (get_merge_request_conflicts, ID 89) that agents can use to retrieve raw conflict data from merge requests with conflicts.

Architecture

CustomService implementation that reuses existing infrastructure:

┌─────────────────────────────────────────┐
│   MergeRequests::Conflicts::ListService │  ← Existing logic
│   (battle-tested, powers web UI)        │
└─────────────────────────────────────────┘
            ↑                    ↑
            │                    │
     ┌──────┴─────┐       ┌──────┴──────────┐
     │ Controller │       │ CustomService   │ ← NEW
     │ (Web UI)   │       │ (MCP Tool)      │
     └────────────┘       └─────────────────┘

Both call the exact same service - no duplication!

Changes

  • Service: GetMergeRequestConflictsService CustomService (106 lines)
  • Registration: Register tool in CUSTOM_TOOLS manager and catalog (ID 89)
  • Tests: Comprehensive specs for service (27 examples, 456 lines)
  • Integration: Updated MCP tools list test to include new tool

Features

  • Returns raw git conflict markers (<<<<<<<, =======, >>>>>>>) in plain text
  • Supports both numeric project IDs and full paths
  • Handles multiple error cases gracefully (no conflicts, missing branches, not found)
  • Authorization via can_push_to_branch? (Developer+ access) - matches controller behavior
  • Explicit project validation for better error messages
  • Memoization for both project and merge request lookups
  • Defensive nil guard for file content
  • Returns ALL conflict data (no distinction between "simple" and "complex")
  • Lower token consumption for LLM usage compared to structured JSON

Output Format

The MCP tool returns conflict data as plain text with simple file markers and git conflict markers:

# File: files/ruby/regex.rb
<<<<<<< files/ruby/regex.rb
def project_name_regexp
  /\A[a-zA-Z0-9][a-zA-Z0-9_\-\. ]*\z/
end
=======
def project_name_regex
  %r{\A[a-zA-Z0-9][a-zA-Z0-9_\-\. ]*\z}
end
>>>>>>> files/ruby/regex.rb

# File: another/file.rb
[next conflict...]

For renamed files, both paths are shown:

# File: old/path.rb -> new/path.rb
[conflict content...]

Benefits:

  • Native git conflict format - LLMs understand it naturally
  • Lower token consumption - no JSON structure overhead
  • Simpler implementation - no parsing required
  • Handles ALL conflicts - no parser errors on complex cases
  • Matches developer experience - same format seen in editors
  • Clear file markers without pretending to be git diff output

Testing

All 27 specs passing:

  • Version registration and metadata
  • Tool execution with conflicts
  • Error handling (merge states, missing MRs, missing branches, invalid projects)
  • Authorization (Developer+ required, Guest denied)
  • Memoization (both project and merge request)
  • Raw conflict output format with file markers

Testing approach:

  1. Creates MRs with conflicts using existing test branches (conflict-resolvable, conflict-start)
  2. Calls the MCP tool and verifies returned plain text format
  3. Tests various error scenarios and merge states
  4. Verifies authorization at different permission levels
  5. Tests project validation with both numeric IDs and full paths

See spec/services/mcp/tools/get_merge_request_conflicts_service_spec.rb for complete test coverage.

Security

  • Explicit project validation before merge request lookup for clear error messages
  • Defensive nil guard for file.content to handle unexpected edge cases
  • Uses ResourceFinder concern patterns for secure project lookups
  • Authorization matches controller behavior (can_push_to_branch?)
  • No exposure of sensitive data - only returns conflict content

Implementation Details

Memoization:

  • Project lookup memoized to avoid duplicate database queries
  • Merge request lookup memoized with explicit cache key (project_id_merge_request_iid)

Authorization:

  • Overrides base authorize! method to use can_push_to_branch?
  • Same authorization as conflicts controller (Developer+ access required)
  • Early project validation provides clear error messages

Error Handling:

  • Project not found: "project not found or inaccessible"
  • Merge request not found: "merge request not found"
  • No conflicts: "Merge request does not have conflicts"
  • Missing branches: "Cannot retrieve conflicts: missing branches or diff refs"
  • Unchecked merge status: Returns status with explanation

Closes

Closes #588528 (closed)

Edited by Kai Armstrong

Merge request reports

Loading
Loading